53

コードでC++の静的変数を初期化すると、初期化は関数を初めて実行したときにのみ実行されることに気付きました。

それはクールですが、それはどのように実装されていますか?それはある種のねじれたifステートメントに変換されますか?(値が指定されている場合は、..)

void go( int x )
{
    static int j = x ;
    cout << ++j << endl ; // see 6, 7, 8
} 

int main()
{
    go( 5 ) ;
    go( 5 ) ;
    go( 5 ) ; 
}
4

4 に答える 4

60

ifはい、通常、内部ブールフラグを持つ暗黙のステートメントに変換されます。したがって、最も基本的な実装では、宣言は通常、次のようなものに変換されます

void go( int x ) {
  static int j;
  static bool j_initialized;

  if (!j_initialized) {
    j = x;
    j_initialized = true;
  }

  ...
} 

その上、静的オブジェクトに自明ではないデストラクタがある場合、言語は別の規則に従う必要があります。そのような静的オブジェクトは、構築の逆の順序で破棄する必要があります。構築順序は実行時にしか分からないため、破棄順序も実行時に定義されます。したがって、重要なデストラクタを使用してローカルの静的オブジェクトを構築するたびに、プログラムはそれをある種の線形コンテナに登録する必要があります。これは、後でこれらのオブジェクトを適切な順序で破棄するために使用されます。

言うまでもなく、実際の詳細は実装に依存します。


コンパイル時の定数で初期化された「プリミティブ」タイプの静的オブジェクト(int例のように)に関しては、コンパイラは起動時にそのオブジェクトを自由に初期化できることを追加する価値があります。違いに気付くことはありません。ただし、「非プリミティブ」オブジェクトを使用したより複雑な例を挙げると、

void go( int x ) {
  static std::string s = "Hello World!";
  ...

上記のアプローチifは、オブジェクトがコンパイル時の定数で初期化されている場合でも、生成されたコードで見つかると予想されるものです。

あなたの場合、初期化子はコンパイル時に不明です。つまり、コンパイラは初期化を遅らせ、その暗黙的なを使用する必要がありますif

于 2011-04-06T13:59:01.303 に答える
8

はい、コンパイラは通常、「これは初期化されていますか?」という非表示のブール値を生成します。flag と、if関数が実行されるたびに実行される an です。

ここにはさらに読み物があります:静的変数の初期化はコンパイラによってどのように実装されていますか?

于 2011-04-06T13:59:15.637 に答える
3

確かに「ひねくれたif」ではありますが、そのひねりは想像以上かもしれません…。

AndreyT の回答に対する ZoogieZork のコメントは、重要な側面に触れています。GCCを含む一部のコンパイラでは、静的ローカル変数の初期化はデフォルトでスレッドセーフです(コンパイラのコマンドライン オプションで無効にできます)。その結果、比較的遅くなる可能性のあるスレッド間同期メカニズム (ミューテックスまたはある種のアトミック操作) を使用しています。. 関数でそのような操作を明示的に使用することに快適ではない場合 (パフォーマンスに関して)、変数の遅延初期化に代わる影響の少ない代替手段があるかどうかを検討する必要があります (つまり、自分でスレッドセーフな方法で明示的に構築します)。どこかで一度だけ)。ただし、これが問題になるほどパフォーマンスに敏感な関数はほとんどありません。プログラムが遅すぎてプロファイラーがその領域をフィンガーリングしない限り、一日を台無しにしたり、コードをより複雑にしたりしないでください。

于 2011-04-06T14:19:02.587 に答える
1

これらは、C ++標準で義務付けられているため、一度だけ初期化されます。これがどのように行われるかは、完全にコンパイラベンダー次第です。私の経験では、ローカルの隠しフラグが生成され、コンパイラによって使用されます。

于 2011-04-06T14:40:53.280 に答える