web-dev-qa-db-ja.com

静的C ++クラスメンバーはいつ初期化されますか?

これに対する簡単な答えはないように見えますが、静的クラスフィールドにいつアクセスできるかについて安全に行うことができる仮定はありますか?

編集:唯一の安全な仮定は、プログラムが開始する前にすべての静的変数が初期化されるように思われます(mainの呼び出し)。したがって、他の静的初期化コードから静的変数を参照しない限り、心配する必要はありませんか?

60
Tony the Pony

標準は、2つのことを保証します。同じ翻訳単位(通常は.cppファイルを意味します)で定義されたオブジェクトは、定義の順に初期化されます(宣言ではない):

3.6.2

静的ストレージ期間(basic.stc.static)を持つオブジェクトのストレージは、他の初期化が行われる前にゼロで初期化されます(dcl.init)。ゼロ初期化および定数式を使用した初期化は、総称して静的初期化と呼ばれます。他のすべての初期化は動的初期化です。静的なストレージ期間が定数式(expr.const)で初期化されたPODタイプ(basic.types)のオブジェクトは、動的な初期化が行われる前に初期化されます。同じ翻訳単位の名前空間スコープで定義され、動的に初期化される静的ストレージ期間を持つオブジェクトは、その定義が翻訳単位に現れる順序で初期化されるものとします。

他の保証されていることは、翻訳ユニットからの静的オブジェクトの初期化は、この翻訳ユニットのオブジェクトまたは関数を使用する前に行われることです。

Mainの最初のステートメントの前に、名前空間スコープのオブジェクトの動的初期化(dcl.init、class.static、class.ctor、class.expl.init)が行われるかどうかは、実装定義です。 mainの最初のステートメントの後、ある時点まで初期化が延期される場合、初期化されるオブジェクトと同じ変換単位で定義された関数またはオブジェクトの最初の使用前に発生します。

他には何も保証されていません(特に、異なる翻訳単位で定義されたオブジェクトの初期化の順序は実装定義です)。

編集スマのコメントで指摘されているように、mainが入力される前に初期化されることも保証されています。

54
Tadeusz Kopec

これらは、プログラムの開始前(つまり、mainが入力される前)に初期化されます。

1つのCPPファイルに(静的データの)2つ以上の定義がある場合、それらはファイルで定義されている順序で初期化されます(ファイルで以前に定義された/より高い定義は、次の前に初期化されます1つです)。

複数のCPPファイルに(静的データの)2つ以上の定義がある場合、CPPファイルが処理される順序は未定義/実装固有です。これは、グローバル変数のコンストラクター(プログラムの開始前に呼び出される)が、まだ作成されていない可能性のある別のCPPファイルで定義された別のグローバル変数を参照する場合の問題です。ただし、Meyersのアイテム47 Effective C++(タイトルは使用前にグローバルオブジェクトが初期化されていることを確認する)は回避策を説明しています...

  • ヘッダーファイルで静的変数を定義します(静的なので、リンカに文句を言わずに複数のインスタンスを保持できます)

  • その変数のコンストラクターに必要なものを呼び出させます(特に、ヘッダーで宣言されたグローバルシングルトンを構築します)

...それはいくつかのシステムヘッダーファイルで使用されるかもしれないテクニックです。 cinグローバル変数が静的変数のコンストラクターでさえ使用される前に初期化されるようにします。

18
ChrisW

編集での最終的な結論は正しいです。しかし、問題はクラス自体が静的であるということです。私のコードには、他のグローバルデータ/クラスの静的メンバーを参照しないクラスの静的メンバーが含まれていると言う方が簡単ですが、このルートをとるとすぐに問題が発生します。クラスの静的データメンバーではなく、クラスの静的ラッパーメソッドを持つことは、実際に役立つとわかったアプローチの1つです。これらのメソッドは、静的オブジェクトを内部に保持できます。例えば.

TypeX* Class2::getClass1Instance()
{
    static TypeX obj1;
    return &obj1;
}
</ code>

注:以前の回答は次のとおりです。

他の保証されていることは、翻訳単位からの静的オブジェクトの初期化は、この翻訳単位からオブジェクトまたは関数を使用する前に行われることです

これは完全に正しいわけではなく、標準はここで誤って推測されています。 mainに入る前に変換ユニットの関数が呼び出された場合、これは当てはまらない場合があります。

3
Paani

実行中はいつでもアクセスできると思います。未定義のままであるのは、静的変数の初期化順序です。

1
erelender

それらは実装ファイル(.c/cpp/cc)ファイルで初期化できます。コンパイラが複数の定義について文句を言うので、それらを.hで初期化しないでください。

通常、これらはmainの前に初期化されますが、順序は不明であるため、依存関係は避けてください。メンバー関数内で確実にアクセスできます。静的メンバーの初期化の順序は不明です。静的メンバーを初期化されたかどうかをチェックする静的関数に静的メンバーをカプセル化することをお勧めします。

1
vehomzzz

この質問に対する完全に簡単な答えはありませんが、基本的には、制御がプログラムのエントリポイント(メイン)に渡される直前に初期化されます。それらが初期化される順序は(私の知る限り)未定義であり、コンパイラ固有のものである可能性があります。

編集:明確にするために、あなたの追加された仮定は正しいです。メインエントリの後でアクセスするだけであれば、いつ/どのように初期化されるかを心配する必要はありません。その時間までに初期化されます。

0
Dentoid