web-dev-qa-db-ja.com

C ++での静的オブジェクトの破棄順序

静的オブジェクトが破棄される順序を制御できますか?希望の注文を強制する方法はありますか?たとえば、特定のオブジェクトを最後に、または少なくとも別の静的オブジェクトの後に破棄するように指定するには、どうすればよいですか?

48
Gal Goldman

静的オブジェクトは、構築の逆の順序で破棄されます。そして、建設の順序を制御することは非常に困難です。確信できる唯一のことは、同じコンパイル単位で定義された2つのオブジェクトが定義順に構築されることです。他のものは多かれ少なかれランダムです。

51
Arkadiy

これに対する他の答えは、それは実行できないと主張しています。そして、仕様によると、それらは正しいです-しかし、そこにはisを実行させるトリックがあります。

single静的変数のみを作成します。クラスまたは構造体には、通常は静的変数を作成する他のすべてのものが含まれています。

class StaticVariables {
    public:
    StaticVariables(): pvar1(new Var1Type), pvar2(new Var2Type) { };
    ~StaticVariables();

    Var1Type *pvar1;
    Var2Type *pvar2;
};

static StaticVariables svars;

StaticVariablesのコンストラクタとデストラクタで、必要な任意の順序で変数を作成できます。さらに重要なのは、destroyを任意の順序で作成することです。これを完全に透過的にするには、次のように、変数への静的参照も作成できます。

static Var1Type &var1(*svars.var1);

Voilà-完全な制御。 :-)とはいえ、これは余分な作業であり、通常は不要です。しかしisが必要な場合、それについて知ることは非常に役に立ちます。

24
Head Geek

短い答え:一般的には違います。

少し長い答え:単一の翻訳単位内のグローバル静的オブジェクトの場合、初期化の順序は上から下で、破棄の順序はまったく逆です。複数の翻訳単位間の順序は定義されていません。

特定の注文が本当に必要な場合は、自分でこれを作成する必要があります。

11
gimpf

静的オブジェクトは、構築された順序と逆の順序で破棄され(たとえば、最初に構築されたオブジェクトが最後に破棄される)、項目47で説明されている手法を使用して、静的オブジェクトが構築される順序を制御できます。 "使用する前にグローバルオブジェクトが初期化されていることを確認してください" 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クラスには、静的参照カウンターがあります。最初のInitAインスタンスが構築されると、GlobalBインスタンスが構築される前に保証されます。InitAコンストラクターは、globalAインスタンスが確実に初期化されるようにするために必要なことをすべて実行できます。

11
ChrisW

標準の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

...詳しくはドキュメントをご覧ください

4
David Mott
3
Martin York

いいえ、できません。静的オブジェクトの構築/破壊の他のものに決して頼るべきではありません。

いつでもシングルトンを使用して、グローバルリソースの構築/破棄の順序を制御できます。

0
Martin Cote

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番目のインスタンスを実際に作成しようとしても停止しませんが、アサーションが失敗すると失敗します。私の経験ではそれはうまくいきます。

0
Paolo.Bolzoni

Tの代わりに_static std::optional<T>_を使用すると、同様の機能を効果的に実現できます。変数で行うように初期化し、間接指定で使用し、_std::nullopt_(または、ブーストの場合は_boost::none_)を割り当てて破棄します。

これは、メモリが事前に割り当てられているという点でポインタを持つのとは異なります。したがって、破棄して(おそらく後で)再作成した場合、オブジェクトには同じアドレス(保持できる)があり、その時点で動的割り当て/割り当て解除のコストはかかりません。

_boost::optional<T>_/_std::_がない場合は、_std::experimental::_を使用します。

0
lorro