4

私は最近、 Static Initialization Order Fiascoを痛感しました。「初期化順序は翻訳単位全体で定義されていません」というルールが、子クラスの静的メンバーに必要な親クラスの静的メンバーにも適用されるかどうか疑問に思っています。

たとえば、(簡潔にするために、すべての # ガードとインクルードを除く)があるとします

// a.h
class A {
    static int count;
    static int register_subclass();
};

// a.cpp
int A::count = 0;
int A::register_subclass() {
    return count ++;
}

そして、のサブクラスA

// b.h
class B : public A {
    static int id;
};

// b.cpp
int B::id = A::register_subclass();

ここには2つの翻訳単位があり、一方は静的オブジェクトに依存し、他方は初期化時に静的オブジェクトに依存しています...静的初期化順序の大失敗のインスタンスのようです。

私の質問は:それは実際に安全ですか?

つまり、後者が初期化される前にB::idコピーされたジャンクが含まれる可能性がないことを保証できますか? A::count私自身のテストでは、A常に最初に初期化されるように見えますが、初期化の順序でノイズを導入して、動作が未定義の場合に失敗する可能性を高める方法がわかりません。

4

2 に答える 2

4

一般に、基本クラスと派生クラスの静的な初期化順序に依存するのは安全ではありません。の静的初期化が のA前に発生するという保証はありませんBこれが静的初期化順序 fiascoの定義です。

最初の使用イディオムで構成を使用できます。

// a.h
class A {
private:
    static int& count();
protected:
    static int register_subclass();
};

// a.cpp
int& A::count() {
    static int count = 0;
    return count;
}

int A::register_subclass() {
    return count()++;
}

// b.h
class B : public A {
public:
    static int id;
};

// b.cpp
int B::id = A::register_subclass();

ライブデモ。

更新:しかし、そう言って、ボグダンはコメントで指摘しました

標準の [3.6.2] によれば、この具体例の初期化の順序は保証されています。継承とは何の関係もありませんが、の初期化A::count定数の初期化であり、使用する動的初期化の前に行われることが保証されていB::idます。

しかし、そのようなイントラキャッシーを完全に把握していない限り、最初に使用するイディオムの構文を使用することをお勧めします。

この場合は問題ありませんがA::register_subclass、マルチスレッド コンテキストのような関数には注意してください。複数のスレッドが同時に呼び出すと、何かが起こる可能性があります。

于 2015-10-18T11:41:07.893 に答える
0

「初期化順序は翻訳単位全体で定義されていません」というルールが、子クラスの静的メンバーに必要な親クラスの静的メンバーにも適用されるかどうか疑問に思っています。

はい、そうです。

静的データ メンバーが継承階層 (または、実際にはそれらをカプセル化するクラス) に関連付ける唯一の方法は、完全修飾名を使用することです。それらの定義と初期化は、これをまったく認識していません/気にしていません。

于 2015-10-18T12:55:28.223 に答える