閑古鳥

オールドプログラマの日記。プログラミングとか病気(透析)の話とか。

コンストラクタでのコンストラクタ呼び出し

ややこしいタイトルですが、要するに以下のようなコードのことを指しています。

struct ctor
{
    ctor() : m_num(0)
    {
        ctor(123); //< これ
    }
    ctor(int num)
    {
        m_num = num;
    }

    ~ctor()
    {
        std::cout << m_num << std::endl;
    }
    
    int m_num;
};

void main()
{
    ctor test;
}

デフォルトコンストラクタの中 (「これ」のとこ) で、別の引数を取るコンストラクタをさもメンバ関数のように呼び出していますが、これは、やってはいけません。いや、別にやってもいいんですが……。

コンストラクタは呼び出された時点で、そのクラスのインスタンスを作成します。どういうことかというと、この場合、「これ」の部分で新たに ctor クラスのインスタンスが一時変数として作成されてしまうわけです。

上の例ではデストラクタにメンバ変数 m_num の値を画面出力させていますが、このコードを実行するとまず画面に「123」と出力され、その後に「0」と出力されるはずです。最初の表示は、一時変数のデストラクタ呼び出しによって行われるもので、つまりコンストラクタを呼ぶことで、新しいインスタンスができあがっていることを証明しているわけですね。

でもって、 main 関数の中で、デフォルトコンストラクタを使用して構築した test 変数は、コンストラクタの中では一時変数を作っただけで m_num メンバなどは全く触っていないことになるため、 m_num は初期値である 0 のまま、画面出力されてしまうと。

たまにクラスの初期化を行う際に、「コンストラクタが複数あるけれど、それぞれのコンストラクタに同じ処理がある」、といった場合に処理をひとつの場所にまとめるために上記のようなコードを書いている例をたまに見かけますが、そういう意図の場合、思い通りの結果になりませんので注意しましょう、と。素直にメソッドを作るのが正解でしょう。

昔常駐先でこういうコードを見た時に「こういうやり方があるのか」なんて思ってしまって、危うくダークサイドに落ちるところでしたよ。