オブジェクトの作成に関する小さな質問。次の2つのクラスがあるとします。
struct A{
A(){cout << "A() C-tor" << endl;}
~A(){cout << "~A() D-tor" << endl;}
};
struct B : public A{
B(){cout << "B() C-tor" << endl;}
~B(){cout << "~B() D-tor" << endl;}
A a;
};
そして、主にB
のインスタンスを作成します:
int main(){
B b;
}
B
はA
から派生し、A
型のフィールドも持っていることに注意してください
私はルールを理解しようとしています。オブジェクトを構築するとき、最初にその親コンストラクターを呼び出し、その逆の場合はそのことを知っています。
フィールド(A a;
この場合)? B
が作成されると、いつA
のコンストラクターを呼び出しますか?初期化リストを定義していませんが、何らかのデフォルトリストがありますか?そして、デフォルトのリストがない場合はどうなりますか?そして、破壊についての同じ質問。
class
で始まります。複数のベースclass
esがある場合、構築は左端のベースから始まります。 (サイドノート:virtual
継承がある場合、より高い優先度が与えられます)。class
自体が構築されますイニシャライザリストに関係なく、呼び出し順序は次のようになります。
class A
のコンストラクターclass B
のa
という名前のフィールド(class A
型)が構築されますclass B
のコンストラクターの派生仮想/多重継承がないと仮定すると(かなり複雑になります)、ルールは単純です:
覚えておくべき重要なことの1つは、ステップ4までは、オブジェクトはまだクラスのインスタンスではないということです。コンストラクターの実行が開始されてからこのタイトルを取得するからです。つまり、メンバーのコンストラクター中に例外がスローされた場合、オブジェクトのデストラクターは実行されず、既に構築された部分(メンバーや基底クラスなど)のみが破棄されます。これは、メンバーまたは基本クラスのコンストラクターでオブジェクトの仮想メンバー関数を呼び出す場合、呼び出される実装は派生したものではなく、基本的なものになることも意味します。覚えておくべきもう1つの重要なことは、初期化リストにリストされているメンバーは、初期化リストに現れる順序ではなく、クラスで宣言されている順序で構築されることです(幸運なことに、メンバーをリストすると、クラス宣言とは異なる順序で)。
また、コンストラクタコードの実行中にthis
オブジェクトが既に最終クラスを取得している場合(たとえば、仮想ディスパッチに関して)でも、コンストラクタ実行を完了します。コンストラクターが実行を完了した場合にのみ、オブジェクトインスタンスはインスタンス間の本当のファーストクラスの市民です...その前は(正しいクラスを持っているにもかかわらず) "wanna-be instance"にすぎません。
破壊はまったく逆の順序で行われます。最初にオブジェクトデストラクターが実行され、次にそのクラスが失われます(つまり、オブジェクトのこの時点からベースオブジェクトと見なされます)。最も抽象的な親まで実行されます。コンストラクターについては、オブジェクトの仮想メンバー関数を(直接または間接的に)ベースまたはメンバーデストラクタで呼び出すと、クラスデストラクタが完了したときにオブジェクトがクラスタイトルを失ったため、実行される実装が親実装になります。
基本クラスは常にデータメンバーの前に構築されます。データメンバーは、クラスで宣言された順序で構築されます。この順序は、初期化リストとは関係ありません。データメンバが初期化されるとき、パラメータの初期化リストを調べ、一致するものがない場合はデフォルトコンストラクタを呼び出します。データメンバーのデストラクタは、常に逆の順序で呼び出されます。
基本クラスコンストラクターは、常にfirst.soを実行します。ステートメントを記述するときにB b;
最初にA
のコンストラクターが呼び出され、次にB
クラスのコンストラクターが呼び出されます。したがって、コンストラクターからの出力は次のようなシーケンスになります。
A() C-tor
A() C-tor
B() C-tor
#include<iostream>
class A
{
public:
A(int n=2): m_i(n)
{
// std::cout<<"Base Constructed with m_i "<<m_i<<std::endl;
}
~A()
{
// std::cout<<"Base Destructed with m_i"<<m_i<<std::endl;
std::cout<<m_i;
}
protected:
int m_i;
};
class B: public A
{
public:
B(int n ): m_a1(m_i + 1), m_a2(n)
{
//std::cout<<"Derived Constructed with m_i "<<m_i<<std::endl;
}
~B()
{
// std::cout<<"Derived Destructed with m_i"<<m_i<<std::endl;
std::cout<<m_i;//2
--m_i;
}
private:
A m_a1;//3
A m_a2;//5
};
int main()
{
{ B b(5);}
std::cout <<std::endl;
return 0;
}
この場合の答えは2531です。ここでコンストラクタを呼び出す方法:
同方向のデストラクタが呼び出されます:
この例では、m_A1およびm_A2の構築は、初期化リストの順序ではなく、宣言の順序とは無関係です。
変更されたコードからの出力は次のとおりです。
A() C-tor
A() C-tor
B() C-tor
~B() D-tor
~A() D-tor
~A() D-tor