
こんにちは。コードレビューの1つで、staticキーワードをいつ使用するかをよく理解しているのではなく、多くの、そして何を隠すかという考えに出くわしました。この記事では、staticキーワードに関する私の知識と情報を共有したいと思います。 , , ++. /++ . , static ++, . ++, , , , .
static?
Staticは、要素に特別な特性を与えるために使用されるC ++キーワードです。静的要素の場合、メモリは1回だけ割り当てられ、これらの要素はプログラムが終了するまで存在します。これらの要素はすべて、ヒープやスタックではなく、.dataおよび.bssと呼ばれる特別なメモリセグメントに格納されます(静的データが初期化されているかどうかによって異なります)。次の図は、プログラムメモリの一般的なレイアウトを示しています。

どこで使われていますか?
以下は、静的がプログラムで使用される方法と場所の図です。

そして今、私は図に示されているすべてを詳細に説明しようとします。行く!
関数内の静的変数
静的変数は、関数内で使用される場合、一度だけ初期化され、その後、その値を保持します。これらの静的変数は、スタックではなく静的メモリ領域(.dataまたは.bss)に格納されます。これにより、変数の値をプログラムの存続期間を通じて格納および使用できます。2つのほぼ同一のプログラムとその動作を見てみましょう。唯一の違いは、一方が静的変数を使用し、もう一方が使用しないことです。
最初のプログラム:
#include <iostream>
void counter() {
static int count = 0; // 4
std::cout << count++;
}
int main() {
for (int i = 0; i < 10; ++i) {
counter();
}
return 0;
}
プログラム出力:
0123456789
2番目のプログラム:
#include <iostream>
void counter() {
int count = 0; // 4
std::cout << count++;
}
int main() {
for (int i = 0; i < 10; ++i) {
counter();
}
return 0;
}
プログラム出力:
000000000
4行目でstaticを 使用しない場合、メモリの割り当てと変数カウントの初期化は、counter()が呼び出されるたびに発生し、関数が終了するたびに破棄されます。ただし、変数を静的にすると、初期化後(counter()関数が最初に呼び出されたとき)、countはmain()関数の終わりにスコープされ、変数はcounter()の呼び出し間でその値を保持します。
静的クラスオブジェクト
クラスの静的オブジェクトは、上記の通常の静的変数と同じプロパティを持っています。.dataまたは.bssメモリセグメントに保存され、起動時に作成され、プログラムの終了時に破棄され、一度だけ初期化されます。オブジェクトは通常どおり初期化されます-クラスコンストラクターを介して。静的クラスオブジェクトの例を考えてみましょう。
#include <iostream>
class Base { // 3
public:
Base() { // 5
std::cout << "Constructor" << std::endl;
}
~Base() { // 8
std::cout << "Destructor" << std::endl;
}
};
void foo() {
static Base obj; // 14
} // 15
int main() {
foo(); // 18
std::cout << "End of main()" << std::endl;
return 0;
}
プログラム出力:
コンストラクター
メイン()
デストラクタの終わり
3 Base ( 5) ( 8). . 14 obj Base. foo() 18.
- , , foo() 15, , .. . , , .
#include <iostream>
class Base {
public:
Base() {
std::cout << "Constructor" << std::endl;
}
~Base() {
std::cout << "Destructor" << std::endl;
}
};
void foo() {
Base obj;
} // 15
int main() {
foo();
std::cout << "End of main()" << std::endl;
return 0;
}
foo()関数で変数を作成するときにstatic を削除すると、関数が呼び出されるたびに15行目でオブジェクトの破棄が発生します。この場合、プログラムの出力は、スタックにメモリが割り当てられているローカル変数に対して非常に期待されます。
コンストラクタ
デストラクタ
メインの終わり()
静的クラスメンバー
以前の使用例と比較して、クラスの静的メンバーは理解するのが少し難しいです。理由を見てみましょう。次のプログラムがあるとします。
#include <iostream>
class A { // 3
public:
A() { std::cout << "Constructor A" << std::endl; }
~A() { std::cout << "Destructor A" << std::endl; }
};
class B { // 9
public:
B() { std::cout << "Constructor B" << std::endl; }
~B() { std::cout << "Destructor B" << std::endl; }
private:
static A a; // 15 ()
};
int main() {
B b; // 19
return 0;
}
この例では、静的クラスメンバー(15行目)を使用してクラスA(3行目)とクラスB(9行目)を作成しました。私たちは、オブジェクトの作成と仮定Bを上のライン19は、オブジェクトが作成されますAのライン15を。これは、クラスの非静的メンバーを使用した場合に当てはまります。ただし、プログラムの出力は次のようになります。
コンストラクターB
デストラクタB
この動作の理由は、クラスの静的メンバーはオブジェクトの初期化に依存しないため、コンストラクターで初期化されないためです。それら。上のライン15、定義が外に使用して、クラス起こらなければならないので、我々は、それを定義していない、オブジェクトを宣言しているスコープ解決演算子(::)。クラスBのメンバーを定義しましょう。
#include <iostream>
class A {
public:
A() { std::cout << "Constructor A" << std::endl; }
~A() { std::cout << "Destructor A" << std::endl; }
};
class B {
public:
B() { std::cout << "Constructor B" << std::endl; }
~B() { std::cout << "Destructor B" << std::endl; }
private:
static A a; // 15 ()
};
A B::a; // 18 ()
int main() {
B b;
return 0;
}
ここで、18行目で静的クラスメンバーを定義すると、次のプログラム出力が表示されます。
コンストラクターA
コンストラクターB
デストラクタB
デストラクタA
クラスメンバーは、クラスBのすべてのインスタンスで同じになることを覚えておく必要があります。クラスBのオブジェクトを3つ作成した場合、静的クラスメンバーのコンストラクターは1回だけ呼び出されます。これが私が話していることの例です:
#include <iostream>
class A {
public:
A() { std::cout << "Constructor A" << std::endl; }
~A() { std::cout << "Destructor A" << std::endl; }
};
class B {
public:
B() { std::cout << "Constructor B" << count++ << std::endl; }
~B() { std::cout << "Destructor B" << --count << std::endl; }
private:
static A a; //
static int count; //
};
A B::a; //
int B::count = 1; //
int main() {
B b1, b2, b3;
return 0;
}
プログラム出力:
コンストラクターA
コンストラクターB1
コンストラクターB2
コンストラクターB3
デストラクタB3
デストラクタB2
デストラクタB1
デストラクタA
静的関数
静的関数はCからC ++に移行しました。デフォルトでは、Cのすべての関数はグローバルであり、同じプロジェクトの2つの異なる.c(.cpp)ファイルに同じ名前の2つの関数を作成する場合、この関数を示すエラーが発生します。すでに定義されています(致命的なエラーLNK1169:1つ以上の複数定義されたシンボルが見つかりました)。以下は、1つのプログラムの3つのファイルのリストです。
// extend_math.cpp
int sum(int a, int b) {
int some_coefficient = 1;
return a + b + some_coefficient;
}
// math.cpp
int sum(int a, int b) {
return a + b;
}
// main.cpp
int sum(int, int); // declaration
int main() {
int result = sum(1, 2);
return 0;
}
この問題を修正するために、関数の1つをstaticとして宣言します。たとえば、これは次のとおりです。
// extend_math.cpp
static int sum(int a, int b) {
int some_coefficient = 1;
return a + b + some_coefficient;
}
, , . sum() math.cpp . , static , , , , , (.h).
, inline static, , . (.cpp), #include , . , , .. include .cpp .
- ()
クラスのオブジェクトを作成せずに、静的メンバー関数を使用できます。静的関数には、クラス名とスコープ解決演算子(::)を使用してアクセスします。静的メンバー関数を使用する場合、次のような制限があります。
- 関数内では、静的データメンバー、他の静的メンバー関数、およびクラス外からの他の関数にのみアクセスできます。
- 静的メンバー関数には、それらが存在するクラスのスコープがあります。
- この関数を呼び出すオブジェクトを作成しないため、クラスのthisポインターにアクセスできません。
次の例を見てみましょう。
#include <iostream>
class A {
public:
A() { std::cout << "Constructor A" << std::endl; }
~A() { std::cout << "Destructor A" << std::endl; }
static void foo() { // 8
std::cout << "static foo()" << std::endl;
}
};
int main() {
A::foo(); // 14
return 0;
}
クラスA、上のライン8、我々は静的メンバ関数を有するFOOを() 。14行目では、クラス名とスコープ解決演算子を使用して関数を呼び出し、次のプログラム出力を取得します。
static foo()
出力から、オブジェクトの作成がなく、コンストラクター/デストラクターが呼び出されていないことがわかります。
場合のfoo()メソッドは非静的だった、コンパイラが式にエラーをスローしていましたライン14上のため、非静的メソッドにアクセスするには、オブジェクトを作成する必要があります。
結論
– « static , ». , , .
:
- , . , , , . , , .
- , , .. , . , , .. .
- static Singleton, , . , -. Singleton , , .
- 以前の状態をオブジェクトのどこかに保存せずに関数を1回だけ実行するために、静的変数が使用される場合があります。「関数内の静的変数」のセクションで例を見ることができます。ただし、これはあまり適切なアプローチではなく、マルチスレッドを使用している場合は、長時間のバグハンティングにつながる可能性があります。
- 実際には、C ++プログラマーは、実行するためにオブジェクトを作成する必要のない通常の関数の代わりに、静的メンバー関数を使用することがよくあります。
C ++のstaticキーワード に関する私の記事を楽しんでいただけたでしょうか。どんな批判やアドバイスもいただければ幸いです。ありがとうございます!