402

static変数が関数のスコープ内で宣言されている場合、変数は一度だけ初期化され、関数呼び出し間でその値を保持します。その寿命は正確には何ですか?そのコンストラクタとデストラクタはいつ呼び出されますか?

void foo() 
{ 
    static string plonk = "When will I die?";
}
4

5 に答える 5

286

関数static変数の有効期間は、プログラム フローが最初に[0]宣言に遭遇したときに始まり、プログラムの終了時に終了します。これは、それが実際に構築された場合にのみ、ランタイムがそれを破棄するために何らかのブックキーピングを実行する必要があることを意味します。

さらに、標準では、静的オブジェクトのデストラクタはそれらの構築の完了とは逆の順序で実行する必要があると述べているため[1]、構築の順序は特定のプログラムの実行に依存する可能性があるため、構築の順序を考慮する必要があります。 .

struct emitter {
    string str;
    emitter(const string& s) : str(s) { cout << "Created " << str << endl; }
    ~emitter() { cout << "Destroyed " << str << endl; }
};

void foo(bool skip_first) 
{
    if (!skip_first)
        static emitter a("in if");
    static emitter b("in foo");
}

int main(int argc, char*[])
{
    foo(argc != 2);
    if (argc == 3)
        foo(false);
}

出力:

C:>sample.exe foo で
作成 foo
で破棄

C:>sample.exe 1
if で
作成 foo
で作成 foo で作成
ifで作成

C:>sample.exe 1 2
Created in foo
if Destroyed in fooで作成
if
Destroyed in foo

[0]C++98 [2]には複数のスレッドへの参照がないため、マルチスレッド環境でこれがどのように動作するかは指定されておらず、Roddyが言及しているように問題になる可能性があります。

[1] C++98セクション3.6.3.1 [basic.start.term]

[2]C++11 では、静的はスレッド セーフな方法で初期化されます。これはMagic Staticsとも呼ばれます。

于 2008-10-29T12:16:04.987 に答える
132

モッティは順序については正しいですが、他にも考慮すべき点がいくつかあります。

コンパイラは通常、非表示のフラグ変数を使用して、ローカルの静的変数が既に初期化されているかどうかを示します。このフラグは、関数へのすべてのエントリでチェックされます。明らかに、これは小さなパフォーマンス ヒットですが、さらに懸念されるのは、このフラグがスレッド セーフであることが保証されていないことです。

上記のようにローカルの static があり、foo複数のスレッドから呼び出される場合、競合状態が発生plonkして、正しく初期化されなかったり、複数回初期化されたりする可能性があります。また、この場合、plonkそれを構築したスレッドとは異なるスレッドによって破棄される可能性があります。

標準が言っていることにもかかわらず、ローカルの静的破棄の実際の順序には非常に注意を払っています。なぜなら、破棄された後も静的が有効であることに無意識のうちに依存している可能性があり、これを追跡するのは本当に難しいからです。

于 2008-10-29T12:24:55.733 に答える
10

既存の説明は、6.7 にある標準の実際のルールなしでは完全ではありません。

静的ストレージ期間またはスレッドストレージ期間を持つすべてのブロックスコープ変数のゼロ初期化は、他の初期化が行われる前に実行されます。該当する場合、静的ストレージ期間を持つブロック スコープ エンティティの一定の初期化は、そのブロックが最初に入力される前に実行されます。実装は、実装が名前空間スコープで静的またはスレッドストレージ期間を持つ変数を静的に初期化することを許可されているのと同じ条件下で、静的またはスレッドストレージ期間を持つ他のブロックスコープ変数の早期初期化を実行することが許可されています。それ以外の場合、そのような変数は、コントロールがその宣言を最初に通過するときに初期化されます。そのような変数は、初期化の完了時に初期化されたと見なされます。例外をスローして初期化が終了した場合、初期化は完了していないため、次に制御が宣言に入ったときに再試行されます。変数の初期化中に制御が同時に宣言に入った場合、同時実行は初期化の完了を待ちます。変数の初期化中に制御が宣言に再帰的に入る場合、動作は未定義です。

于 2014-09-11T21:08:25.050 に答える
8

FWIW、Codegear C ++ Builderは、標準に従って期待される順序で破棄しません。

C:\> sample.exe 1 2
Created in foo
Created in if
Destroyed in foo
Destroyed in if

...これは破壊命令に依存しないもう1つの理由です!

于 2008-10-29T16:27:17.290 に答える