質問
静的オブジェクトが破棄される順序を制御できますか? 希望する順序を強制する方法はありますか?たとえば、特定のオブジェクトを最後に、または少なくとも別の静的オブジェクトの後に破棄することを何らかの方法で指定するには?
解決
静的オブジェクトは、構築の逆の順序で破壊されます。そして、建設の順序を制御することは非常に困難です。確認できる唯一のことは、同じコンパイル単位で定義された2つのオブジェクトが定義順に構築されることです。それ以外のものは多かれ少なかれランダムです。
他のヒント
これに対する他の答えは、それができないということです。仕様によると、彼らは正しいのですが、それを可能にするトリックが あります。
通常、静的変数を作成する他のすべてのものを含むクラスまたは構造体の単一静的変数のみを作成します。
class StaticVariables {
public:
StaticVariables(): pvar1(new Var1Type), pvar2(new Var2Type) { };
~StaticVariables();
Var1Type *pvar1;
Var2Type *pvar2;
};
static StaticVariables svars;
必要な順序で変数を作成できます。さらに重要なのは、StaticVariables
のコンストラクタとデストラクタで、必要な順序で変数を破棄できることです。これを完全に透過的にするには、次のように変数への静的参照を作成することもできます。
static Var1Type &var1(*svars.var1);
ボイル<!>#224; -トータルコントロール。 :-)とはいえ、これは余分な作業であり、通常は不要です。ただし、必要な場合は 、知っておくと非常に便利です。
簡単な答え:一般に、いいえ。
少し長めの答え:単一の翻訳単位のグローバルな静的オブジェクトの場合、初期化の順序は上から下、破壊の順序はまったく逆です。複数の翻訳単位間の順序は未定義です。
特定の注文が本当に必要な場合は、自分で注文する必要があります。
静的オブジェクトは、構築された順序と逆の順序で破棄されます(たとえば、最初に構築されたオブジェクトが最後に破棄されます)。また、以下で説明する手法を使用して、静的オブジェクトの構築順序を制御できますアイテム47、<!> quot; グローバルオブジェクトを使用する前に初期化する <!> quot; Meyersの本 Effective C ++ で。
たとえば、特定のオブジェクトを最後に、または少なくとも別の静的オブジェクトの後に破棄することを何らかの方法で指定するには?
他の静的オブジェクトの前に構築されていることを確認します。
建設順序を制御するにはどうすればよいですか?すべての統計が同じdllにあるわけではありません。
同じDLLにないという事実を(簡単にするために)無視します。
マイヤーズのアイテム47(4ページの長さ)の私の言い換えは次のとおりです。グローバルがこのようなヘッダーファイルで定義されていると仮定すると...
//GlobalA.h
extern GlobalA globalA; //declare a global
...このようなインクルードファイルにコードを追加します...
//GlobalA.h
extern GlobalA globalA; //declare a global
class InitA
{
static int refCount;
public:
InitA();
~InitA();
};
static InitA initA;
これの効果は、GlobalA.hを含むファイル(たとえば、2番目のグローバル変数を定義するGlobalB.cppソースファイル)が、InitAクラスの静的インスタンスを定義することです。それ以外の場合は、そのソースファイル内(2番目のグローバル変数の前など)。
このInitAクラスには、静的な参照カウンターがあります。 GlobalBインスタンスが構築される前に保証される最初のInitAインスタンスが構築されると、InitAコンストラクターは、globalAインスタンスが初期化されるようにするために必要なことをすべて実行できます。
標準C ++でそれを行う方法はありませんが、特定のコンパイラ内部について十分な実用知識がある場合は、おそらくそれを達成できます。
Visual C ++では、静的init関数へのポインターは.CRT$XI
セグメント(Cタイプの静的initの場合)または.CRT$XC
セグメント(C ++タイプの静的initの場合)にあります。リンカーはすべての宣言を収集し、アルファベット順にマージします。
#pragma init_seg
たとえば、ファイルAのオブジェクトをファイルBの前に作成する場合:
ファイルA.cpp:
#pragma init_seg(".CRT$XCB")
class A{}A;
ファイルB.cpp:
#pragma init_seg(".CRT$XCC")
class B{}B;
.CRT$XCB
は、.CRT$XCC
の前にマージされます。 CRTが静的init関数ポインターを反復処理すると、ファイルBの前にファイルAが検出されます。
Watcomでは、セグメントはXIであり、#pragma initializeのバリエーションが構築を制御できます。
#pragma initialize before library
#pragma initialize after library
#pragma initialize before user
...詳細についてはドキュメントを参照
読み取り:
SO初期化順序
いいえ、できません。静的オブジェクトの構築/破棄に依存することは絶対にしないでください。
いつでもシングルトンを使用して、グローバルリソースの構築/破棄の順序を制御できます。
本当にmain
の前に変数を初期化する必要がありますか?
そうでない場合は、簡単なイディオムを使用して、実際に構築と破壊の順序を簡単に制御できます。こちらをご覧ください:
#include <cassert>
class single {
static single* instance;
public:
static single& get_instance() {
assert(instance != 0);
return *instance;
}
single()
// : normal constructor here
{
assert(instance == 0);
instance = this;
}
~single() {
// normal destructor here
instance = 0;
}
};
single* single::instance = 0;
int real_main(int argc, char** argv) {
//real program here...
//everywhere you need
single::get_instance();
return 0;
}
int main(int argc, char** argv) {
single a;
// other classes made with the same pattern
// since they are auto variables the order of construction
// and destruction is well defined.
return real_main(argc, argv);
}
実際にクラスの2番目のインスタンスを作成しようとすることは停止しませんが、実行するとアサーションが失敗します。私の経験では、それは正常に動作します。
static std::optional<T>
の代わりにT
を使用すると、同様の機能を効果的に実現できます。変数で行うように初期化し、インダイレクションで使用し、std::nullopt
(またはブーストの場合はboost::none
)を割り当てて破棄します。
それは、事前に割り当てられたメモリを持っているという点で、ポインタを持つこととは異なります。したがって、それを破壊した場合は<!> amp; (おそらく後で)オブジェクトを再作成すると、オブジェクトは同じアドレス(保持できる)を持ち、その時点での動的割り当て/割り当て解除のコストはかかりません。
boost::optional<T>
/ std::
がない場合はstd::experimental::
を使用します。