C ++関数の静的変数の有効期間はどのくらいですか?


373

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

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

回答:


257

関数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
Created in if
Created in foo
Destroyed in foo
Destroyed in if

C:> SAMPLE.EXE 1 2は、
FOOで作成され
た場合に作成され
た場合で破壊
FOOで破壊します

[0]以来、C ++は98 [2]は、これがマルチスレッド環境で動作あろう方法、複数のスレッドへの参照は指定されていない、とのように問題となることがロディに言及しています。

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

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


2
c'tor / d'tor副作用のない単純型の場合、グローバル単純型と同じ方法で初期化することは簡単な最適化です。これにより、分岐、フラグ、および破壊の順序の問題が回避されます。それは彼らの寿命が違うと言っているのではありません。
ジョンマクファーレン

1
関数が複数のスレッドによって呼び出される可能性がある場合、これは静的宣言がC ++ 98のミューテックスによって保護される必要があることを確認する必要があることを意味しますか?
allyourcode 2014年

1
これらのオブジェクトはグローバルではないため、「グローバルオブジェクトのデストラクタは、構築の完了とは逆の順序で実行する必要があります」はここでは適用されません。静的またはスレッドストレージ期間を持つローカルの破壊順序は、純粋なLIFOよりもかなり複雑です。セクション3.6.3を参照してください[basic.start.term]
Ben Voigt

2
「プログラムの終了時」という語句は厳密には正しくありません。動的にロードおよびアンロードされるWindows dllの静的についてはどうですか?明らかに、C ++標準はアセンブリをまったく処理しませんが(そうするのは良いことですが)、標準がここで言うことを正確に説明するとよいでしょう。「プログラムの終了時」というフレーズが含まれている場合、動的にアンロードされたアセンブリを使用したC ++の実装は技術的に不適合になります。
ロジャーサンダース

2
@Motti標準で動的ライブラリが明示的に許可されているとは思いませんが、これまで、その実装と矛盾するものが特に標準にあるとは信じていませんでした。もちろん、ここの言語を厳密に言うと、静的オブジェクトを他の方法で以前に破棄することはできないと述べておらず、メインから戻ったりstd :: exitを呼び出したりしたときに静的オブジェクトを破棄する必要があるだけです。かなりいい線だと思います。
ロジャーサンダース

125

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

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

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

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


68
C ++ 0xでは、静的初期化がスレッドセーフである必要があります。だから注意してください、しかし物事は良くなるだけです。
deft_code

破壊命令の問題は、少しのポリシーで回避できます。静的/グローバルオブジェクト(シングルトンなど)は、メソッド本体の他の静的オブジェクトにアクセスしてはなりません。それらは、メソッドでの後でのアクセスのために参照/ポインタを格納できるコンストラクタでのみアクセスされます。これは完璧ではありませんが、99のケースを修正する必要があり、キャッチできないケースは明らかに怪しいため、コードレビューでキャッチする必要があります。言語ではポリシーを適用できないため、これはまだ完全な修正ではありません
deft_code

私は少し初心者ですが、このポリシーを言語で適用できないのはなぜですか?
cjcurrie 2013年

9
C ++ 11以降、これは問題ではなくなりました。それに応じてモッティの答えが更新されます。
Nilanjan Basu

10

既存の説明は、6.7にある標準からの実際のルールがなければ、完全ではありません。

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


8

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

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

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


57
良い議論ではありません。これは、このコンパイラを使用しないほうがよいと思います。
マーティンヨーク

26
うーん。理論的に移植可能なコードだけでなく、実際の移植可能なコードの作成に関心がある場合は、言語のどの領域で問題が発生する可能性があるかを知っておくと役に立ちます。C ++ Builderがこれを処理しないという点でユニークだったとしたら、私は驚きます。
ロディ

17
「コンパイラーが問題を引き起こすのはどのコンパイラーであり、言語のどの領域で使用されているのか」と表現する場合を除き、同意します;-P
Steve Jessop

0

静的変数は、一度遊びに来ているプログラムの実行を開始すると、それは、プログラムの実行が終了するまで、引き続き使用可能。

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


これは、関数スコープの変数には当てはまりません
awerries
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.