次のコードがどのように実行されるかについて、いくつか質問があります
using namespace std;
class A
{
int i;
public:
A() { i = 7; cout << 1 << f() << i; }
A(int i) :i(i) { cout << 1 << f() << i; }
char f() { return 'A'; }
};
class B : public virtual A
{
int i;
public:
B(int i) : A(i), i(++i) { cout << 2 << i; }
virtual char f() { return 'B'; }
};
class C : public virtual A {
public:
C(int i) : A(i) { cout << 3 << i; }
virtual char f() { return 'C'; }
};
class D :public A {
public:
D(int i) { cout << 4 << i; }
virtual char f() { return 'D'; }
};
class E : public B, public C, public D {
public:
E() : B(2), C(3), D(4) { cout << 5; }
virtual char f() { return 'E'; }
};
int main()
{
E e;
return 0;
}
したがって、出力は「1A723331A7445」になります。
そうです、あなたが本当にめちゃくちゃなタイプ階層を作成したと言うことから始めましょう。初期化の順序を理解しようとした場合、この例は混乱を招く可能性があります。
とにかく、物事を明確にするために、私はあなたのコードを変更し、/
各c'torprintステートメントの最後にスラッシュ文字を追加しました。したがって、行のどの部分が各c'torに属しているかをより簡単に識別できます。これにより、次の出力が得られます。
1A7 / 23/33 / 1A7 / 44/5
初期化の順序に入る前に、指定したすべての仮想関数が動的にディスパッチされないことを知っておく必要があります。C'tor本体の仮想関数は静的にバインドされます。したがって、私たちの意図と目的のために、コードで呼び出される仮想関数は実際にはありません。
さて、C ++標準を引用すると、これは初期化順序が決定される方法です([class.base.init] / 13):
非委任コンストラクターでは、初期化は次の順序で進行します。
まず、最も派生したクラスのコンストラクターの場合のみ、仮想基本クラスは、基本クラスの有向非巡回グラフの深さ優先の左から右へのトラバーサルに表示される順序で初期化されます。ここで、「左から右」 」は、派生クラスbase-specifier-list内の基本クラスの出現順序です。
次に、直接基本クラスは、base-specifier-listに表示される宣言の順序で初期化されます(mem-initializersの順序に関係なく)。
次に、非静的データメンバーは、クラス定義で宣言された順序で初期化されます(ここでも、mem-initializersの順序に関係なく)。
最後に、コンストラクター本体の複合ステートメントが実行されます。
それでは、初期化を分解してみましょう。
1)仮想A
あなたがのリスト初期化子メンバーでそれを指定しなかったため、サブオブジェクトがデフォルトの構築、でE()
、それは実行A()
のために共有されているオブジェクトのためにB
とC
、そしてプリント1A7/
。
2)ここで、のc'torB
が呼び出され、で実行さB(int i)
れi = 2
ます。に設定さB::i
れ3
、c'tor本体が印刷され23/
ます。
3)C
を呼び出すことによって構築されるC(int i)
とi = 3
。これは印刷し33/
ます。
4)次に、を作成しD
ます。あなたが呼ぶそうD(int i)
でi = 4
。非仮想からD
継承するためA
、今すぐA
構築が必要な別個のサブオブジェクトがあります。
メンバー初期化子リストでパラメーターを指定しなかったためA
、デフォルトで作成されます。これは印刷し1A7/
ます。
これで、本体がD(int i)
実行され、印刷され44/
ます。
5)最後に、の本体E()
が呼び出され、印刷され5
ます。
この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。
侵害の場合は、連絡してください[email protected]
コメントを追加