メンバー関数の静的変数


158

メンバー関数の静的変数がC ++でどのように機能するかを誰かが説明できますか?

次のクラスがあるとします。

class A {
   void foo() {
      static int i;
      i++;
   }
}

の複数のインスタンスを宣言した場合、1つのインスタンスをA呼び出すと、すべてのインスタンスのfoo()静的変数が増加しiますか?それとも呼ばれたものだけですか?

各インスタンスには独自ののコピーがあると想定していましたがi、私が持っているいくつかのコードをステップ実行すると、そうでないことを示しているようです。

回答:


169

以来は、class A非テンプレートクラスであり、A::foo()非テンプレート関数です。static int iプログラム内のコピーは1つだけです。

Aオブジェクトのインスタンスは同じオブジェクトに影響しi、の存続期間はiプログラム全体を通して残ります。例を追加するには:

A o1, o2, o3;
o1.foo(); // i = 1
o2.foo(); // i = 2
o3.foo(); // i = 3
o1.foo(); // i = 4

3
良い例をありがとう!static int iインスタンスに固有のスコープを作成する方法を実際に実現する方法があるので、eg o1.foo(); // i = 1および$o2.foo(); // i = 1...?
Stingery 2014年

14
これはあなたが探しているスタイルではないかもしれませんが、iaをクラスAのプライベートデータメンバーにすると、説明した効果が得られます。名前の競合が心配な場合m_は、iのステータスを示すなどの接頭辞を追加できます。
カールモリス

137

static残念ながら、C ++ではキーワードにはいくつかの無関係な意味があります

  1. データメンバーに使用する場合、データはインスタンスではなくクラスに割り当てられます。

  2. 関数内のデータに使用する場合、データは静的に割り当てられ、ブロックが最初に入力されたとき初期化され、プログラムが終了するまで続きます。また、変数は関数内でのみ表示されます。ローカル静的のこの特別な機能は、シングルトンの遅延構築を実装するためによく使用されます。

  3. コンパイルユニットレベル(モジュール)で使用すると、変数はグローバル(つまり、main実行後に割り当てられ初期化され、main終了後に破棄される)のようですが、変数は他のコンパイルユニットではアクセスまたは表示できません

それぞれの用途で最も重要な部分を強調しました。エクスポートされていないクラス宣言も可能にする名前のない名前空間のために、使用(3)はやや推奨されません。

コードでは、staticキーワードは意味番号2で使用され、クラスやインスタンスとは関係ありません...これは関数の変数であり、コピーは1つしかありません。

正しくiammilindが言ったように、ただし、関数がテンプレート関数である場合、その変数の複数のインスタンスがあった可能性があります(その場合、実際には、関数自体がプログラムの多くの異なるコピーに存在する可能性があるためです)。その場合でもコースのクラスとインスタンスは無関係です...次の例を参照してください:

#include <stdio.h>

template<int num>
void bar()
{
    static int baz;
    printf("bar<%i>::baz = %i\n", num, baz++);
}

int main()
{
    bar<1>(); // Output will be 0
    bar<2>(); // Output will be 0
    bar<3>(); // Output will be 0
    bar<1>(); // Output will be 1
    bar<2>(); // Output will be 1
    bar<3>(); // Output will be 1
    bar<1>(); // Output will be 2
    bar<2>(); // Output will be 2
    bar<3>(); // Output will be 2
    return 0;
}

41
+1 keyword static unfortunately has a few different unrelated meanings in C++:)
iammilind

これを読んだ後、世界は非常に理にかなっています。ありがとう
Erin

テンプレートのトリックが好きです。それを使う言い訳を見つけるのが待ちきれません。
トマーシュZato -復活モニカ

誰かが「名前のない名前空間を支持してやや落胆した」という言及を得ましたか?
オースティンマートン、2015年

3
@austinmarton:「スタティックの使用による「ローカルから変換単位への変換」はC ++では非推奨です。代わりに無名の名前空間を使用してください(8.2.5.1)」というフレーズが私の版のC ++プログラミング言語に掲載されています(10番目の印刷物、1999年9月) 819ページ。–
6502

2

関数内の静的変数

  • 静的変数は関数の内部で作成され、スタックではなくプログラムの静的メモリに格納されます。

  • 静的変数の初期化は、関数の最初の呼び出しで行われます。

  • 静的変数は複数の関数呼び出しで値を保持します

  • 静的変数の寿命はプログラムです

ここに画像の説明を入力してください

#include <iostream>

using namespace std;

class CVariableTesting 
{
    public:
    
    void FuncWithStaticVariable();
    void FuncWithAutoVariable();

};

void CVariableTesting::FuncWithStaticVariable()
{
    static int staticVar = 0; //staticVar is initialised by 0 the first time
    cout<<"Variable Value : "<<staticVar<<endl;
    staticVar++;
}
void CVariableTesting::FuncWithAutoVariable()
{
    int autoVar = 0;
    cout<<"Variable Value : "<<autoVar<<endl;
    autoVar++;
}
    

int main()
{
    CVariableTesting objCVariableTesting;
    cout<<"Static Variable";
    objCVariableTesting.FuncWithStaticVariable();
    objCVariableTesting.FuncWithStaticVariable();
    objCVariableTesting.FuncWithStaticVariable();
    objCVariableTesting.FuncWithStaticVariable();
    objCVariableTesting.FuncWithStaticVariable();
    
    cout<<endl;
    cout<<"Auto Variable";
    objCVariableTesting.FuncWithAutoVariable();
    objCVariableTesting.FuncWithAutoVariable();
    objCVariableTesting.FuncWithAutoVariable();
    objCVariableTesting.FuncWithAutoVariable();
    objCVariableTesting.FuncWithAutoVariable();
    
    return 0;
}

出力:

静的変数

変数値:0
変数値:1
変数値:2
変数値:3
変数値:4

自動変数

変数値:0
変数値:0
変数値:0
変数値:0
変数値:0


-2

簡単な答え:

静的変数は、それらがclass(テンプレート化されていない)関数または(テンプレート化されていない)関数のメンバーであるかどうかに関係なく、技術的には、スコープがclassまたは関数に制限されるグローバルラベルのように動作します。


9
いいえ。グローバルはプログラムの起動時に初期化され、関数staticは最初の使用時に初期化されます。これは大きな違いです。
6502

私はこれが何が起こるか考えていません。ただし、これはとにかくコンパイラ固有のものでなければなりません。
0xbadf00d

2
次に、あなたは間違っていると考えます。C++標準の3.6.1は、静的ストレージ期間を持つ名前空間スコープのオブジェクトの構築が起動時に発生することを指示しています。6.7(4)は、一般に「...このような変数は、最初にコントロールがその宣言を通過するときに初期化されます。このような変数は、初期化の完了時に初期化されたと見なされます」と規定しています。ちなみに、この初回使用時の初期化は、遅延シングルトン構成を実装するのに非常に便利です。
6502

3.7.4:「該当する場合、静的ストレージ期間のあるブロックスコープエンティティの定数初期化(3.6.2)は、そのブロックが最初に入力される前に実行されます。実装は、他のブロックスコープ変数の初期化を実行することが許可されています名前空間スコープ(3.6.2)で静的またはスレッドのストレージ期間で変数を静的に初期化することが許可されているのと同じ条件下での静的またはスレッドのストレージ期間。
0xbadf00d

1
奇妙なことに、1)定数の初期化の場合、最初にブロックに入る前にローカルスタティックを初期化できるかどうかは関係ありません(変数はブロック内でのみ表示され、定数の初期化では副作用はありません)。2)あなたの投稿では、定数初期化について何も言われていません。3)ローカルスタティックはMyClass& instance(){ static MyClass x("config.ini"); return x; }、次のような非定数の初期化に非常に役立ちます。ローカルスタティックは、あなたが何を言っているにも関わらず、単にグローバルのようではないため、シングルスレッドで使用できる有効な移植可能な実装です。
6502
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.