他の 2 つの回答で既に述べたように、デストラクタが実行されるとオブジェクトの型が変化します。デストラクタが完了すると、その型のオブジェクトは存在しなくなり、そのベース サブオブジェクトのみが存在します (それらのデストラクタが完了するまで)。
この回答の理由は、興味深い実験を提案するためです。このコードの出力はどうなるでしょうか? (まあ、3 つの答えはすべて既に述べていますが、この実験自体は興味深いものです):
#include <iostream>
struct base {
static void print_type( base const & b ) { // [1]
std::cout << b.type() << std::endl;
}
virtual std::string type() const { // [2]
return "base";
}
virtual ~base() { print_type( *this ); }
base() { print_type( *this ); }
};
struct derived : base {
std::string type() const {
return "derived";
}
~derived() { print_type( *this ); }
derived() { print_type( *this ); }
};
struct most_derived : derived {
std::string type() const {
return "most_derived";
}
~most_derived() { print_type( *this ); }
most_derived() { print_type( *this ); }
};
int main() {
most_derived md;
base::print_type( md );
}
ノート:
さらに面白いことに、 の呼び出しprint_type
もコンストラクターに追加されます。この関数は、その特定の時点でのオブジェクトの動的タイプの検証として機能します。関数print_type
(独立した関数であり、別の翻訳単位で実装されている可能性があります -- コンパイラがその内部を見ないようにするため)。関数のコンパイル中、コンパイラはそれがコンストラクタ、デストラクタの内部から呼び出されたのか、それらの外部から呼び出されたのかを認識できないため、生成されたコードは動的ディスパッチ メカニズムを使用する必要があり、関数の各ポイントで最終的なオーバーライドにディスパッチされます。時間。
コードの有効性については、§12.7/2 によって保証されています。
クラス X のオブジェクトを参照するポインター (左辺値) を、X の直接または間接基底クラス B へのポインター (参照) に明示的または暗黙的に変換するには、X の構築と、その直接または間接基底のすべての構築B から直接的または間接的に派生するクラスが開始され、これらのクラスの破棄が完了していない必要があります。そうでない場合、変換によって未定義の動作が発生します。オブジェクト obj の直接の非静的メンバーへのポインターを形成する (またはその値にアクセスする) には、obj の構築が開始され、その破棄が完了していない必要があります。それ以外の場合、ポインター値の計算 (またはメンバー値へのアクセス) は完了していません未定義の動作が発生します。
base&
への呼び出しでのへの変換は、各print_type
オブジェクトの構築が開始された後、各オブジェクトの破棄が完了する前に実行されるため有効です( eachは、プログラム内の の各サブオブジェクトを参照します)。most_derived