C ++静的初期化の順序
-
05-07-2019 - |
質問
C ++で静的変数を使用するとき、しばしば1つの変数を初期化して別の変数をコンストラクターに渡したいと思うようになります。つまり、互いに依存する静的インスタンスを作成したいのです。
単一の.cppまたは.hファイル内では、これは問題ではありません。インスタンスは宣言された順に作成されます。ただし、静的インスタンスを別のコンパイル単位のインスタンスで初期化する場合、順序を指定することは不可能なようです。その結果、天候に応じて、他のインスタンスに依存するインスタンスが構築され、その後にのみ他のインスタンスが構築される可能性があります。その結果、最初のインスタンスが誤って初期化されます。
静的オブジェクトが正しい順序で作成されるようにする方法を知っている人はいますか?解決策を長い間探して、すべてを試してみました(シュワルツカウンターソリューションを含む)が、実際に機能するものがあるのではないかと疑い始めました。
1つの可能性は、静的関数メンバーのトリックです:
Type& globalObject()
{
static Type theOneAndOnlyInstance;
return theOneAndOnlyInstance;
}
実際、これは機能します。残念ながら、globalObject.MemberFunction()の代わりにglobalObject()。MemberFunction()を記述する必要があります。その結果、やや紛らわしく洗練されていないクライアントコードになります。
更新:ご意見ありがとうございます。残念ながら、実際に自分の質問に答えたようです。私はそれと一緒に暮らすことを学ぶ必要があると思います...
解決
自分の質問に答えました。静的な初期化の順序は未定義であり、静的な初期化(つまり、リファクタリングを完全に行わない)を行う最もエレガントな方法は、関数で初期化をラップすることです。
https://isocpp.org/wikiから始まるC ++ FAQ項目を読む/ faq / ctors#static-init-order
他のヒント
たぶん、多くのグローバルな静的変数が必要かどうかを再検討する必要があります。特に有用な場合もありますが、特に、一部の静的変数が他の静的変数に依存していることがわかった場合は、より小さなローカルスコープにリファクタリングする方がはるかに簡単です。
しかし、あなたは正しい、初期化の特定の順序を保証する方法はないので、あなたの心が設定されているなら、あなたが述べたように、関数で初期化を維持することがおそらく最も簡単な方法です。
実際、これは機能します。残念ながら、globalObject.MemberFunction()の代わりにglobalObject()。MemberFunction()を記述する必要があります。その結果、やや紛らわしく洗練されていないクライアントコードになります。
しかし、最も重要なことは、それが機能すること、そしてそれが失敗の証拠であることです。正しい使用法をバイパスするのは簡単ではありません。
プログラムの正確性が最優先事項です。また、私見、上記の()は純粋に文体的です-すなわち。まったく重要ではありません。
プラットフォームによっては、動的初期化が多すぎることに注意してください。動的イニシャライザーに対して実行できるクリーンアップは比較的少量です(こちら)。この問題は、異なるグローバルオブジェクトのメンバーを含むグローバルオブジェクトコンテナを使用して解決できます。したがって、次のものがあります。
Globals & getGlobals ()
{
static Globals cache;
return cache;
}
プログラムのすべてのグローバルオブジェクトをクリーンアップするために、〜Globals()の呼び出しは1つだけです。グローバルにアクセスするには、次のようなものがまだあります:
getGlobals().configuration.memberFunction ();
本当に必要な場合は、これをマクロでラップして、マクロを使用して入力をわずかに節約できます。
#define GLOBAL(X) getGlobals().#X
GLOBAL(object).memberFunction ();
ただし、これは最初のソリューションの構文糖衣です。
実際には、ほとんどのコンパイラ(リンカー)は、順序を指定する(移植性のない)方法をサポートしています。たとえば、Visual Studioでは、 init_seg を使用できます。 a>プラグマを使用して、初期化をいくつかの異なるグループに配置します。私の知る限り、各グループ内で順序を保証する方法はありません。これは移植性がないので、デザインを修正してそれを必要としないかどうかを検討したいかもしれませんが、オプションはあります。
このスレッドの時代を否定し、私が見つけた解決策を提案したいと思います。 私の前に多くの人が指摘したように、C ++は静的な初期化の順序付けのためのメカニズムを提供していません。私が提案するのは、各静的メンバーをクラスの静的メソッド内にカプセル化することです。この静的メソッドはメンバーを初期化し、オブジェクト指向の方法でアクセスを提供します。 「Math」という名前のクラスを定義したいと仮定して、例を挙げましょう。他のメンバーの中でも、「PI」が含まれています:
class Math {
public:
static const float Pi() {
static const float s_PI = 3.14f;
return s_PI;
}
}
s_PIは、Pi()メソッドが(GCCで)初めて呼び出されたときに初期化されます。注意:静的ストレージを持つローカルオブジェクトには、実装依存のライフサイクルがあります。詳細については、 2 。
メソッドで静的をラップすると順序の問題は修正されますが、他の人が指摘しているようにスレッドセーフではありませんが、懸念がある場合はスレッド化することもできます。
// File scope static pointer is thread safe and is initialized first.
static Type * theOneAndOnlyInstance = 0;
Type& globalObject()
{
if(theOneAndOnlyInstance == 0)
{
// Put mutex lock here for thread safety
theOneAndOnlyInstance = new Type();
}
return *theOneAndOnlyInstance;
}