2016-12-24 1 views
10

のは、以下の私が持っているとしましょう:初期化子リストのクラスメンバーを参照するのは未定義の動作ですか?

class A { 
    B member1; 
    C member2; 
public: 
    A(); 
}; 

class B { 
public: 
    C& ref_to_c; 
    B(C& ref_to_c); 
}; 

class C { 
... 
}; 

BはCへの参照は、コンストラクタで提供されている必要があります。クラスAがCを提供する場合、それは言うことですその次...

A() : member1(B(member2)) {} 

としてAの初期化子リストを指定することが合法である、member2は、初期化子リスト相に存在する、またはこの未定義の動作あるのでしょうか?

+1

member1はメンバー2の前に初期化されています。私はUBについてはわかりませんが、何か間違いが起こると思います。 –

+0

BTW、GCC 4.9.2はまったく文句を言わなかった –

+0

@Galik 'member2'は一時的ではなく'B'は' member1'のために初期化され、コピーコンストラクタまたはムーブコンストラクタを呼び出します。ここで間違いはありません。 –

答えて

14

次のように使う初期である:

5の初期化は、次の順序で進行しなければならない:

- まず、のみ以下に説明 として最も派生クラスのコンストラクタのために、仮想基本クラスをしなければなりません の順序で初期化され、 の基底クラスの非循環有向グラフの深さ優先左から右への走査で表示されます。ここで、左から右は の基本クラス名の表示順です。派生クラス 塩基指定子リスト。

- 直接基底クラスは、 の宣言順序で初期化されます(mem-initializersの の順序にかかわらず)。

- 次に、非静的データメンバーが順番に初期化されなければならないが 彼らは(再びかかわらず、MEM-初期化子の オーダーの)クラス定義で宣言されました。

- 最後に、コンストラクタの本体が実行されます。 [注: 宣言の順序は、ベースとメンバ サブオブジェクトが初期化の逆の順序で確実に破棄されるようにするために必要です。基本的にmember1は常にmember2前に初期化されることを意味します]


。したがって、Bのコンストラクタが最初に実行されます。あなたはAのコンストラクタで明示的に逆の順序でそれらを呼びたい場合でも :

A() : member2(foo), member1(bar) {} 

それは違いはありません。さて、初期化されていないオブジェクトを参照すること自体UBではありませんが、Bのコンストラクタに依存する可能性があります。あなたは宣言の順序を切り替える必要があります:あなたはmemebr2への参照を含むmember1を作っている

C member2; 
B member1; 
4

。 これはまだ構築されていませんが、コンパイラはすでにどこにあるかを知っています(リファレンスを提供できるようになりました)。

リファレンスは実際には初期化されていないメモリのエイリアシングであり、member2の構築中に初期化されるため、Btcのように、ある種の式でref_to_cの値にアクセスしようとするとUBになります。それは後で起こります。

同じ問題がBデストラクタで発生します。member2は、ref_to_cより前に破壊されます。

Aでmember2とmember1を入れ替えた方が良いでしょう。そうすれば、構築されたオブジェクトで参照を初期化し、すべての使用法を定義します。