コンストラクター内の仮想メンバー関数の呼び出しが非仮想呼び出しになるのはなぜですか? 質問する

コンストラクター内の仮想メンバー関数の呼び出しが非仮想呼び出しになるのはなぜですか? 質問する

2 つの C++ クラスがあるとします。

class A
{
public:
  A() { fn(); }

  virtual void fn() { _n = 1; }
  int getn() { return _n; }

protected:
  int _n;
};

class B : public A
{
public:
  B() : A() {}

  virtual void fn() { _n = 2; }
};

次のコードを書いたとします。

int main()
{
  B b;
  int n = b.getn();
}

2 に設定されていると予想されるかもしれませんn

nは1 に設定されていることがわかりました。なぜでしょうか?

ベストアンサー1

コンストラクターまたはデストラクタから仮想関数を呼び出すのは危険なので、可能な限り避けてください。すべての C++ 実装では、現在のコンストラクターの階層レベルで定義された関数のバージョンのみを呼び出す必要があります。

C++ FAQ ライトこれについてはセクション 23.7 でかなり詳しく説明されています。フォローアップのために、そのセクション (および FAQ の残りの部分) を読むことをお勧めします。

抜粋:

[...] コンストラクターでは、派生クラスからのオーバーライドがまだ行われていないため、仮想呼び出しメカニズムは無効になっています。オブジェクトはベースから構築され、「派生前のベース」になります。

[...]

破棄は「派生クラスが基底クラスより先に」行われるため、仮想関数はコンストラクターの場合と同様に動作します。つまり、ローカル定義のみが使用され、オブジェクトの (破棄された) 派生クラス部分に触れないようにするためにオーバーライド関数は呼び出されません。

編集ほとんどすべてを修正しました (litb さん、ありがとう)

おすすめ記事