web-dev-qa-db-ja.com

メンバー変数を宣言された順序で初期化する必要があるのはなぜですか?

今日、いくつかのコードを書いていて、奇妙なコンパイルエラーが発生しました。これは、宣言された順序とは異なる順序でメンバー変数を初期化したことが原因のようです。

例:

class Test {
    int a;
    int b;

public:
    Test() : b(1), a(2) {
    }
};

int main() {
    Test test;
    return 0;
}

次に、-Werror -Wallでコンパイルすると:

$ g++ -Werror -Wall test.cpp
test.cpp: In constructor ‘Test::Test()’:
test.cpp:3:9: error: ‘Test::b’ will be initialized after [-Werror=reorder]
test.cpp:2:9: error:   ‘int Test::a’ [-Werror=reorder]
test.cpp:6:5: error:   when initialized here [-Werror=reorder]
cc1plus: all warnings being treated as errors

-WallはGCCに警告で明示的にオーバーザトップを要求していることを理解していますが、それらすべてに理由があると思います。それでは、メンバー変数を初期化する順序はどのように重要なのでしょうか?

55
Brendan Long

その理由は、コンストラクタで初期化する順序ではなく、クラスで宣言された順序で初期化されるためであり、コンストラクタの順序が使用されないことを警告するためです。

これは、bの初期化がaに依存する、またはその逆のエラーを防ぐためです。

この順序付けの理由は、デストラクタが1つしかないため、クラスメンバーを破棄するために「逆順」を選択する必要があるためです。この場合、最も単純な解決策は、クラス内で宣言の順序を使用して、属性が常に正しい逆の順序で破棄されるようにすることでした。

83
Mark B

読みやすさが低下し、誤解を招く可能性があるため、そうすべきではありません。

あなたがした場合:

Test() : b(1), a(b) {}

bの次にaが両方とも1に設定されたように見えますが、実際にはbの初期化されていない値がaの前にb _は1に初期化されます。

32
CB Bailey

実際にコンパイラalwaysは、初期化子を別の順序で記述した場合でも、宣言の順序で変数を初期化します。したがって、宣言の順序で初期化を記述しない場合、初期化子の順序は初期化の順序に適合せず、初期化が相互に依存している場合、微妙なバグにつながる可能性があります。

たとえば、コードを考えます

Test(): b(42), a(b) {}

これはバグです。abの前に初期化されますが、looks OKです。宣言の順序(初期化の順序)で記述すると、バグが明らかになります。

Test(): a(b), b(42) {}

バグはそれよりも微妙な場合があることに注意してください。たとえば、abはコンストラクターで何かを出力するクラス型であると想像してください。そして、「間違った」順序では、実際には逆になったときにbの出力がaの前に表示されるはずだと思います。最初に表示されるaの出力が無効なファイルにつながる場合、それはバグでもありますが、コンストラクターが別の翻訳単位にある場合、コンパイラーが問題に気付く方法はありません(コンパイラーが知ることができないという事実は別として)並べ替えがバグであるかそうでない場合)。したがって、コンパイラは、一致しない順序のすべてのインスタンスについて警告するだけで十分です。

12
celtschk

-Wallは明示的にGCCに警告を出してオーバーザトップを要求していることを認識していますが、それらすべてに理由があると思います。

-壁はほんの始まりです。名前が示すとおり、-Wallはすべての警告を有効にしません。間違いなく「オーバーザオーバー」の警告がいくつかありますが、それらは-Wallが有効にしない警告です。私はいつも-Wall plus他を使用します。

あなたの苦情に関しては、他の人がすでに指摘したように、この警告には非常に正当な理由があります。順序を指定したからといって、コンパイラがその順序を使用するわけではありません。コンパイラが標準に従って使用しなければならない順序は、クラス定義に基づいています。

5
David Hammen