1. ホーム
  2. c++

[解決済み】コンストラクター内での仮想関数の呼び出し

2022-04-17 15:42:55

質問

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();
}

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

ということが判明しました。 n が1になっているのはなぜですか?

解決するには?

コンストラクタやデストラクタから仮想関数を呼び出すことは危険ですので、可能な限り避けるべきです。 すべての C++ 実装は、現在のコンストラクタの階層レベルで定義されたバージョンの関数を呼び出し、それ以上は呼び出さないようにします。

C++ FAQ Lite は、23.7節でかなり詳細にこのことを扱っています。 フォローアップのために、それ(とFAQの残り)を読むことをお勧めします。

抜粋

<ブロッククオート

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

[...]

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

EDIT MostをAllに修正(litbさんありがとうございます)。