初期化 - cppreference.com
提供: cppreference.com
変数の初期化は構築時にその初期値を提供します。
初期値は宣言子または new 式の初期化子節で提供することができます。 また関数呼び出しでも行われます。 関数の引数および関数の戻り値も初期化されます。
それぞれの宣言子に対して、以下のいずれかの初期化子を指定できます。
( expression-list )
|
(1) | ||||||||
= expression
|
(2) | ||||||||
{ initializer-list }
|
(3) | ||||||||
1) 括弧で囲まれた任意の式または波括弧初期化子リストのコンマ区切りのリスト
2) 等号に続く式
3) 波括弧初期化子リスト (式または別の波括弧初期化子リストのコンマ区切りのリスト。 空でも構いません)
文脈によって、初期化子は以下を発生させます。
- 値初期化、例えば
std::string s{}; - 直接初期化、例えば
std::string s("hello"); - コピー初期化、例えば
std::string s = "hello"; - リスト初期化、例えば
std::string s{'a', 'b', 'c'}; - 集成体初期化、例えば
char a[3] = {'a', 'b'}; - 参照初期化、例えば
char& c = a[0];
初期化子が提供されない場合は、デフォルト初期化のルールが適用されます。
非ローカル変数
静的記憶域期間を持つすべての非ローカル変数は、 main 関数の実行が始まる前に、プログラムのスタートアップの一部として初期化されます (遅延されている場合は除きます (後述))。 スレッドローカル記憶域期間を持つすべての非ローカル変数は、スレッドの関数の実行の開始に対して先行配列される、スレッド起動処理の一部として初期化されます。 これらの変数の記憶域クラスについて、初期化は2つの別々のステージで発生します。
静的初期化
1) 可能であれば、まず定数初期化が行われます (それらの状況の一覧については定数初期化を参照してください)。 実際には、定数初期化は、通常、コンパイル時に行われ、事前計算されたオブジェクト表現がプログラムイメージの一部として格納されます。 コンパイラは、それを行わない場合でも、いかなる動的初期化も発生する前にこの初期化が行われることを保証する必要があります。
2) それ以外のすべての非ローカル静的変数およびスレッドローカル変数については、ゼロ初期化が行われます。
実際には、ゼロ初期化される変数は、プログラムイメージの .bss セグメントに配置されます。 これはディスク上のスペースを占めず、プログラムのロード時に OS によってゼロ初期化されます。
動的初期化
すべての静的初期化の完了後、非ローカル変数の動的初期化が以下の状況で発生します。
1) 順序なし動的初期化。 (静的またはスレッドローカルな) 明示的に特殊化されていないクラステンプレートの静的データメンバおよび変数テンプレート (C++14以上)にのみ適用されます。 そのような静的変数の初期化は他のすべての動的初期化に対して不定に配列されます。 ただし変数が初期化される前にプログラムがスレッドを開始した場合、その初期化は配列されません。 (C++17以上) そのようなスレッドローカル変数の初期化は、他のすべての動的初期化に対して配列されません。
|
2) 部分的順序付き動的初期化。 暗黙にまたは明示的に実体化された特殊化でないすべてのインライン変数に適用されます。 部分的順序付きの V がすべての翻訳単位において順序付きまたは部分的順序付きの W より前の場合、 V の初期化は W の初期化に対して先行配列されます (または、プログラムがスレッドを開始する場合は先行発生します)。 |
(C++17以上) |
3) 順序付き動的初期化。 他のすべての非ローカル変数に適用されます。 単一の翻訳単位内においては、これらの変数の初期化は必ずソースコード中に定義が現れた通りの順序に配列されます。 異なる翻訳単位内の静的変数の初期化は不定に配列されます。 異なる翻訳単位内のスレッドローカル変数の初期化は配列されません。
静的およびスレッド記憶域期間を持つ非ローカル変数の初期化が例外によって終了した場合は、 std::terminate が呼ばれます。
早期動的初期化
以下の条件がどちらも真の場合、コンパイラは静的初期化の一部として (本質的にコンパイル時に) 動的初期化される変数を初期化することが許されています。
1) その初期化の動的バージョンが、その初期化に先行する名前空間スコープの他のいかなるオブジェクトの値も変更しない。
2) その初期化の静的バージョンが、もし静的に初期化されることが要求されないすべての変数が動的に初期化されたならばその動的初期化によって生成されたであろう値と同じ値を、その初期化された変数に生成する。
上記のルールのため、何らかのオブジェクト o1 の初期化が名前空間スコープのオブジェクト o2 を参照し、 o2 が動的初期化を要求するかもしれないけれども、同じ翻訳単位内で後に定義されている場合、使用される o2 の値が、完全に初期化された o2 の値になる (コンパイラが o2 の初期化をコンパイル時に昇格したため) か、ゼロ初期化されただけの o2 の値になるかは、未規定です。
inline double fd() { return 1.0; } extern double d1; double d2 = d1; // 未規定。 // d1 が動的初期化される場合は 0.0 に動的初期化され、 // d1 が静的初期化される場合は 1.0 に動的初期化され、 // または 0.0 に静的初期化されます (両方の変数が動的初期化されれば // その値になるであろうから)。 double d1 = fd(); // 1.0 に静的または動的初期化されます。
遅延動的初期化
動的初期化が main 関数 (静的変数の場合) またはスレッドの初期関数 (スレッドローカル変数の場合) の最初の文に対して先行発生するか、遅延されて後続発生するかは、処理系定義です。
非インライン (C++17以上)変数の初期化が遅延されて main 関数またはスレッド関数の最初の文に対して後続発生する場合、それは変数が初期化されたのと同じ翻訳単位内で定義された静的またはスレッドローカル記憶域期間を持つ任意の変数の最初の ODR 使用に対して先行発生します。 ある翻訳単位内から ODR 使用される変数や関数がない場合、その翻訳単位内で定義される非ローカル変数は初期化されることがないかもしれません (これはオンデマンドな動的ライブラリの動作をモデル化します)。 しかし、その翻訳単位から何かが ODR 使用される限り、初期化または破棄が副作用を持つすべての非ローカル変数は、それらがプログラム内で使用されなくても、初期化されます。
|
インライン変数の初期化が遅延される場合、それはその特定の変数の最初の ODR 使用に対して先行発生します。 |
(C++17以上) |
// ー ファイル1 ー #include "a.h" #include "b.h" B b; A::A(){ b.Use(); } // ー ファイル2 ー #include "a.h" A a; // ー ファイル3 ー #include "a.h" #include "b.h" extern A a; extern B b; int main() { a.Use(); b.Use(); } // main に入る前に a が初期化された場合、 A::A() がそれを使用する時点では // b はまだ初期化されていない可能性があります (動的初期化は翻訳単位間では不定に配列されるため)。 // main の最初の文 (ファイル1で定義された関数を ODR 使用し、動的初期化の実行を強制させます) の後の // いずれかの時点で a が初期化された場合、 b は A::A で使用される前に初期化されます。
静的ローカル変数
ローカルな (つまりブロックスコープの) 静的およびスレッドローカル変数の初期化については、静的ローカル変数を参照してください。
クラスメンバ
非静的データメンバはメンバ初期化子リストまたは デフォルトメンバ初期化子で初期化できます。
ノート
非ローカル変数の破棄の順序は std::exit で説明されます。
欠陥報告
以下の動作変更欠陥報告は以前に発行された C++ 標準に遡って適用されました。
| DR | 適用先 | 発行時の動作 | 正しい動作 |
|---|---|---|---|
| CWG 2026 | C++14 | zero-init was specified to always occur first, even before constant-init | no zero-init if constant init applies |