C++では、グローバルおよび静的オブジェクトはmain()関数に入る前に初期化されます。しかし、標準は異なる翻訳単位(コンパイルファイル)におけるオブジェクトの初期化順序を保証していません。これにより「static initialization order fiasco」と呼ばれるバグが発生する可能性があり、あるグローバルオブジェクトが他のオブジェクトを参照しているが、初期化がまだ行われていない場合があります。
問題を回避し、正しい初期化を保証するために、**"construct on first use"**パターンを使用します。これは、静的なローカルオブジェクトへの参照を返す関数を宣言することです(C++11以降、安全にスレッド対応で初回呼び出し時に作成されます)。
// foo.h class Config { public: int value; }; Config& getConfig() { static Config config; config.value = 42; // 初期化が保証されています! return config; }
異なる.cppファイルでグローバルオブジェクトが互いに生成した場合、何が起こりますか?
回答: 結果は未定義であり、初期化されていないオブジェクトへのアクセスが発生する可能性があります。解決策は、直接のグローバル初期化を関数内の静的ローカルオブジェクトを介した遅延初期化に置き換えることです(上記参照)。
逸話
-CRMシステムで2つのモジュールが初期化時に互いに参照するグローバルロガーを宣言しました。アプリケーションの異なるビルドでは初期化の順序が変更され、難解なバグが発生し、クラッシュやヌルポインタへのログ記録が見られました。
逸話
-グラフィックスエンジンでは、リソースのためのグローバルコンテナオブジェクトがグローバルであるリソースマネージャを参照していました。LinuxとWindowsではコンパイラがロード順序を異なる方法で実装しており、同期のずれやさまざまなクラッシュが発生しました。
逸話
-開発者は設定のグローバル初期化を実装し、後に同僚が設定に依存する別のグローバル変数を追加しました。これはローカルでは機能しましたが、異なるモジュールのリンクが行われるCIでは失敗しました。