効果的な C++ では、初期化リスト内のデータ要素を宣言の順序でリストする必要があると言われています。さらに、この理由は、データ要素のデストラクタがコンストラクタの逆の順序で呼び出されるためであると言われています。
しかし、これがどのように問題になるのかわかりません...
効果的な C++ では、初期化リスト内のデータ要素を宣言の順序でリストする必要があると言われています。さらに、この理由は、データ要素のデストラクタがコンストラクタの逆の順序で呼び出されるためであると言われています。
しかし、これがどのように問題になるのかわかりません...
次のことをよく考えてください。
class Class {
Class( int var ) : var1( var ), var2(var1 ) {} // allright
//Class( int var ) : var2( var ), var1(var2 ) {} // var1 will be left uninitialized
int var1;
int var2;
};
2番目の(コメントアウトされた)コンストラクターは問題ないように見えますが、実際var2
には初期化されるだけです-var1
最初に初期var2
化され、その時点ではまだ初期化されていない状態で初期化されます。
メンバー変数がクラス宣言にリストされているのと同じ順序で初期化子をリストすると、そのようなエラーのリスクははるかに低くなります。
メンバーが何らかの形で相互に依存するクラスのオブジェクトでもある場合、構築と破棄の順序が重要になる場合があります。
簡単な例を考えてみましょう:
class MyString {
public:
size_t s_length;
std::string s;
MyString(const char *str) : s(str), s_length(s.length()) {}
};
この例の意図はs_length
、格納された文字列の長さを member が保持することです。ただし、 は の前s_length
に初期化されるため、これは機能しません。したがって、のコンストラクターが実行される前に呼び出します! s
s.length
s
たとえば、次のようなクラスがあるとします。
class X {
int a,b;
X(int i) : b(i),a(b) { } // Constructor
};
クラス X のコンストラクタは、最初に "b" を初期化するように見えますが、実際には宣言順に初期化されます。つまり、最初に「a」を初期化します。ただし、「a」はまだ初期化されていない「b」の値に初期化されるため、「a」はジャンク値を取得します。
破壊は構築の逆であるため、要素は逆の順序で破壊されます。
2 つのメンバーがあるa
としb
ます。b
に依存しますa
が、a
依存しませんb
。
構築するときは、最初に構築a
し、構築できるものが存在するようになりましb
た。a
破壊するとき、最初に破壊すると、これが問題になる場合がありb
ます。しかし、最初に破壊b
し、完全性を確保します。
これは典型的です。たとえば群論では、 の逆数fg
は~g~f
(どこで~f
は の逆数f
)
服を着るときは、まず靴下を履き、次に靴を履きます。服を脱ぐときは、まず靴を脱ぎ、次に靴下を脱ぎます。
また、メンバーのコンストラクターの 1 つが例外をスローした場合も問題になる可能性があります。次に、デストラクタの初期化リストに似たものがないため、既に適切に構築されたすべてのメンバーを何らかの順序で破棄する必要があります。この順序は、クラス宣言でのメンバーの出現の逆順です。例:
#include <iostream>
struct A1 {
A1(int) { std::cout << "A1::A1(int)" << std::endl; }
~A1() { std::cout << "A1::~A1()" << std::endl; }
};
struct A2 {
A2(int) { std::cout << "A2::A2(int)" << std::endl; }
~A2() { std::cout << "A2::~A2()" << std::endl; }
};
struct B {
B(int) { std::cout << "B::B(int)" << std::endl; throw 1; }
~B() { std::cout << "B::~B()" << std::endl; }
};
struct C {
C() : a1(1), a2(2), b(3) { std::cout << "C::C()" << std::endl; } // throw 1; }
~C() { std::cout << "C::~C()" << std::endl; }
A1 a1;
A2 a2;
B b;
};
int main() {
try {
C c;
} catch (int i) {
std::cout << "Exception!\n";
}
}
出力は次のようになります。
A1::A1(int)
A2::A2(int)
B::B(int)
A2::~A2()
A1::~A1()
Exception!
さらに、この理由は、データ要素のデストラクタがコンストラクタの逆の順序で呼び出されるためであると言われています。
Class component order of initialisationでの Steve Jessop のコメントを参照してください。