75

C++ で静的変数を使用する場合、ある変数を初期化して別の変数をそのコンストラクターに渡したいと思うことがよくあります。つまり、相互に依存する静的インスタンスを作成したいと考えています。

単一の .cpp または .h ファイル内では、これは問題ではありません。インスタンスは、宣言された順序で作成されます。ただし、静的インスタンスを別のコンパイル単位のインスタンスで初期化したい場合、順序を指定することは不可能のようです。その結果、天候によっては、別のインスタンスに依存するインスタンスが構築され、その後でのみ別のインスタンスが構築されることがあります。その結果、最初のインスタンスが正しく初期化されません。

静的オブジェクトが正しい順序で作成されるようにする方法を知っている人はいますか? 私は長い間解決策を探し、それらすべて (Schwarz Counter ソリューションを含む) を試してきましたが、本当に機能するものがあるとは思えません。

1 つの可能性は、静的関数メンバーを使用したトリックです。

Type& globalObject()
{
    static Type theOneAndOnlyInstance;
    return theOneAndOnlyInstance;
}

確かに、これは機能します。残念ながら、globalObject.MemberFunction() の代わりに globalObject().MemberFunction() を記述する必要があるため、クライアント コードがやや混乱し、洗練されていません。

更新:ご反応ありがとうございます。残念ながら、私は自分の質問に答えたようです。私はそれと一緒に暮らすことを学ばなければならないと思います...

4

6 に答える 6

71

あなたはあなた自身の質問に答えました。静的な初期化の順序は定義されておらず、これを回避する最も洗練された方法は (静的な初期化を行いながら、つまり完全にリファクタリングしないで)、関数で初期化をラップすることです。

https://isocpp.org/wiki/faq/ctors#static-init-orderから始まる C++ FAQ 項目をお読みください

于 2009-06-17T08:09:05.517 に答える
7

それほど多くのグローバル静的変数が必要かどうかを再検討する必要があるかもしれません。それらは便利な場合もありますが、多くの場合、特に一部の静的変数が他の静的変数に依存している場合は、より小さなローカル スコープにリファクタリングする方がはるかに簡単です。

しかし、そうです、初期化の特定の順序を保証する方法はありません。したがって、あなたの心がそれに設定されている場合は、あなたが述べたように、初期化を関数に保持することがおそらく最も簡単な方法です。

于 2009-06-17T08:12:04.153 に答える
5

確かに、これは機能します。残念ながら、globalObject.MemberFunction()の代わりにglobalObject()。MemberFunction()を作成する必要があるため、クライアントコードがやや混乱し、エレガントではなくなります。

しかし、最も重要なことは、それが機能すること、そしてそれが故障の証拠であるということです。正しい使用法を回避することは容易ではありません。

プログラムの正確さはあなたの最優先事項でなければなりません。また、私見、上記の()は純粋に文体です-すなわち。まったく重要ではありません。

プラットフォームによっては、動的初期化が多すぎることに注意してください。動的初期化子に対して実行できるクリーンアップは比較的少量です(ここを参照)。この問題は、さまざまなグローバルオブジェクトのメンバーを含むグローバルオブジェクトコンテナを使用して解決できます。したがって、次のようになります。

Globals & getGlobals ()
{
  static Globals cache;
  return cache;
}

プログラム内のすべてのグローバルオブジェクトをクリーンアップするための〜Globals()の呼び出しは1つだけです。グローバルにアクセスするには、まだ次のようなものがあります。

getGlobals().configuration.memberFunction ();

本当に必要な場合は、これをマクロでラップして、マクロを使用した入力のほんの少しを節約できます。

#define GLOBAL(X) getGlobals().#X
GLOBAL(object).memberFunction ();

ただし、これは最初のソリューションの単なる構文糖衣です。

于 2009-06-17T10:12:07.953 に答える
5

ほとんどのコンパイラ (リンカー) は、実際には順序を指定する (移植性のない) 方法をサポートしています。たとえば、Visual Studio では、init_segプラグマを使用して、初期化をいくつかの異なるグループに配置できます。私の知る限り、各グループ内での順序を保証する方法はありません。これは移植性がないため、設計を修正して不要にすることができるかどうかを検討する必要があるかもしれませんが、オプションはそこにあります。

于 2009-06-26T14:42:37.417 に答える
1

静的をメソッドにラップすると順序の問題が修正されますが、他の人が指摘しているようにスレッドセーフではありませんが、懸念がある場合はこれを実行してスレッドにすることもできます。

// 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;
}
于 2014-01-29T16:45:07.177 に答える