web-dev-qa-db-ja.com

C ++関数の静的変数の寿命は?

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

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

関数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
fooで作成されました
ifで作成されました
ifで破壊されました
fooで破壊されました

[0]以来C++ 98[2] マルチスレッド環境でこれがどのように振る舞うかについては、マルチスレッドへの言及がないため、未定義であり、 Roddy が述べているように問題になる可能性があります。

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

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

229
Motti

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

コンパイラは通常、ローカルスタティックがすでに初期化されているかどうかを示すために隠しフラグ変数を使用し、このフラグは関数へのすべてのエントリでチェックされます。明らかにこれは小さなパフォーマンスの打撃ですが、もっと心配なことはこのフラグがスレッドセーフであることが保証されていないということです。

もしあなたが上記のようにローカルな静的変数を持っていて、 'foo'が複数のスレッドから呼ばれると、 'plonk'が間違って初期化されたり、何度も初期化されるという競合状態があるかもしれません。またこの場合、 'plonk'はそれを構築したものとは異なるスレッドによって破壊されるかもしれません。

規格が言っているにもかかわらず、ローカルの静的な破棄の実際の順序には細心の注意を払う必要があります。

124
Roddy

既存の説明は、6.7にある標準からの実際の規則なしには完全に完成していません。

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

10
Ben Voigt

FWIW、Codegear C++ Builderは標準に従って予想通りの順序で破壊されません。

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

これは破壊命令に頼らないもう一つの理由です!

8
Roddy

静的変数は、プログラムの実行が開始されると開始され、プログラムの実行が終了するまで利用可能なままです。

静的変数は、メモリのデータセグメントに作成されます。

0