BODY とマークされた行がなければ、これは安全ではないことがわかります。しかし、それで、これは安全ですか?
struct A
{
virtual ~A() { f(); }
virtual void f() = 0;
};
void A::f() {} // BODY
struct B : A
{
void f() {}
};
int main()
{
delete new B;
}
BODY とマークされた行がなければ、これは安全ではないことがわかります。しかし、それで、これは安全ですか?
struct A
{
virtual ~A() { f(); }
virtual void f() = 0;
};
void A::f() {} // BODY
struct B : A
{
void f() {}
};
int main()
{
delete new B;
}
いいえ、それは安全ではありません。A
コンストラクタ (またはデストラクタ) が実行されている間、オブジェクトは typeA
であり、まだ (もはや)B
オブジェクトではありません。への呼び出しf()
は、(まだ) 純粋な仮想関数へのディスパッチを試み、未定義の動作を引き起こします。ほとんどの実装はこれをキャッチし、純粋な仮想関数が呼び出されたことを示すエラー メッセージでアプリケーションを終了します。
編集後:
純粋仮想関数の定義があるという事実は、仮想ディスパッチを経由せずに呼び出すことが合法であることを意味します。動的ディスパッチを使用して純粋仮想関数を呼び出すことは依然として違法です。ただし、コンストラクターを次のように書き換えることができます。
A::~A() { A::f(); } // qualification disables dynamic dispatch
動的ディスパッチがなければ、コードは有効になります。
仮想ディスパッチをバイパスして、定義した関数本体を呼び出す場合は、関数名を修飾する必要があります。
virtual ~A() { A::f(); } // OK.
それ以外の場合、呼び出しは仮想ディスパッチを開始しますが、基本クラスに対してのみです。これは、派生型のオブジェクトがそのベースの前に既に破棄されているためです。
C++11 §12.7/4 はあなたの質問に直接対処します:
仮想関数 (10.3) を含むメンバー関数は、構築または破棄 (12.6.2) 中に呼び出すことができます。クラスの非静的データ メンバーの構築中または破棄中を含め、コンストラクタまたはデストラクタから仮想関数が直接的または間接的に呼び出され、呼び出しが適用されるオブジェクトが構築中のオブジェクト (x と呼ぶ) である場合または破壊の場合、呼び出される関数は、コンストラクタまたはデストラクタのクラスの最終オーバーライドであり、より派生したクラスでそれをオーバーライドするものではありません。仮想関数呼び出しが明示的なクラス メンバー アクセス (5.2.5) を使用し、オブジェクト式が x またはその基本クラス サブオブジェクトの 1 つではなく、x またはその基本クラス サブオブジェクトの 1 つの完全なオブジェクトを参照する場合、動作は未定義です。 .
ただし、§10.4/6 では、純粋な仮想関数でこれを行うことは禁止されています。
メンバ関数は、抽象クラスのコンストラクタ (またはデストラクタ) から呼び出すことができます。そのようなコンストラクタ (またはデストラクタ) から作成 (または破棄) されるオブジェクトに対して、直接的または間接的に純粋仮想関数への仮想呼び出し (10.3) を行うことの効果は定義されていません。
というわけでUBです。
「純粋な仮想」の効果は、仮想ルックアップから関数定義を隠すことです。おそらく未定義の動作の影響を除いて、動的ディスパッチ関数呼び出しから純粋仮想関数の定義に到達することはありません。