ストレージ クラス
C++ の変数宣言のコンテキストにおいて、ストレージ クラスは、オブジェクトの有効期間、リンケージ、およびメモリの場所を制御する型指定子です。 特定のオブジェクトはストレージ クラスを 1 つのみ持つことができます。 ブロック内で定義された変数は、extern
、static
、または thread_local
指定子によって指定されていない限り、自動ストレージを持ちます。 自動オブジェクトおよび変数にはリンケージがないため、ブロックの外側のコードには不可視です。 メモリは、実行がブロックに入ったときに自動的に割り当てられ、ブロックの終了時に割り当て解除されます。
メモ
mutable
キーワードは、ストレージ クラス指定子と見なされることがあります。 ただし、クラス定義のメンバー一覧でのみ使用できます。Visual Studio 2010 以降:
auto
キーワードは C++ のストレージ クラス指定子ではなくなりました。また、register
キーワードは非推奨とされました。 Visual Studio 2017 バージョン 15.7 以降: (/std:c++17
モード以降で使用可能):register
キーワードは、C++ 言語から削除されます。 その使用により、診断メッセージが発生します。// c5033.cpp // compile by using: cl /c /std:c++17 c5033.cpp register int value; // warning C5033: 'register' is no longer a supported storage class
static
static
キーワードを使用すると、変数と関数をグローバル スコープ、名前空間スコープ、およびクラス スコープで宣言することができます。 静的変数は、ローカル スコープでも宣言できます。
静的存続期間は、オブジェクトまたは変数がプログラム起動時に割り当てられ、プログラムの終了時に解放されることを意味します。 外部リンケージは、変数が宣言されたファイルの外部から変数名を参照できることを意味します。 逆に、内部リンケージは、変数が宣言されたファイルの外部でその名前を参照できないことを意味します。 既定では、グローバル名前空間で定義されたオブジェクトまたは変数には静的存続期間と外部リンケージがあります。 static
キーワードは次の状況で使用できます。
ファイル スコープ (グローバル スコープまたは名前空間スコープ、あるいはその両方) で変数または関数を宣言する場合、
static
キーワードは、変数または関数に内部リンケージがあることを指定します。 変数を宣言すると、変数は静的存続期間が設定され、コンパイラによって 0 に初期化されます (別の値を指定していない場合)。関数で変数を宣言する場合、
static
キーワードは、その関数の呼び出しと呼び出しの間に変数がその状態を保持することを指定します。クラス宣言でデータ メンバーを宣言する場合、
static
キーワードは、メンバーの 1 つのコピーがクラスのすべてのインスタンスで共有されることを指定します。static
データ メンバーはファイル スコープで定義する必要があります。const static
として宣言した整数データ メンバーには、初期化子を定義できます。クラス宣言でメンバー関数を宣言する場合、
static
キーワードは、関数がクラスのすべてのインスタンスで共有されることを指定します。static
メンバー関数には暗黙のthis
ポインターがないため、この関数からはインスタンス メンバーにアクセスできません。 インスタンス メンバーにアクセスするには、インスタンスのポインターまたは参照であるパラメーターを指定して関数を宣言します。union
のメンバーをstatic
として宣言することはできません。 ただし、グローバルに宣言された匿名のunion
は、明示的に宣言されたstatic
である必要があります。
この例では、関数内で static
として宣言された変数が、その関数の呼び出しと呼び出しの間、どのようにその状態を保持するかを示します。
// static1.cpp
// compile with: /EHsc
#include <iostream>
using namespace std;
void showstat( int curr ) {
static int nStatic; // Value of nStatic is retained
// between each function call
nStatic += curr;
cout << "nStatic is " << nStatic << endl;
}
int main() {
for ( int i = 0; i < 5; i++ )
showstat( i );
}
nStatic is 0
nStatic is 1
nStatic is 3
nStatic is 6
nStatic is 10
この例は、クラスでの static
の使用例を示しています。
// static2.cpp
// compile with: /EHsc
#include <iostream>
using namespace std;
class CMyClass {
public:
static int m_i;
};
int CMyClass::m_i = 0;
CMyClass myObject1;
CMyClass myObject2;
int main() {
cout << myObject1.m_i << endl;
cout << myObject2.m_i << endl;
myObject1.m_i = 1;
cout << myObject1.m_i << endl;
cout << myObject2.m_i << endl;
myObject2.m_i = 2;
cout << myObject1.m_i << endl;
cout << myObject2.m_i << endl;
CMyClass::m_i = 3;
cout << myObject1.m_i << endl;
cout << myObject2.m_i << endl;
}
0
0
1
1
2
2
3
3
次の例では、メンバー関数内で宣言されたローカル変数 static
を示します。 static
変数は、プログラム全体で使用できます。型のすべてのインスタンスは、static
変数の同じコピーを共有します。
// static3.cpp
// compile with: /EHsc
#include <iostream>
using namespace std;
struct C {
void Test(int value) {
static int var = 0;
if (var == value)
cout << "var == value" << endl;
else
cout << "var != value" << endl;
var = value;
}
};
int main() {
C c1;
C c2;
c1.Test(100);
c2.Test(100);
}
var != value
var == value
C++11 以降では、static
ローカル変数の初期化はスレッド セーフであることが保証されています。 この機能は、静的マジックと呼ばれることがあります。 ただし、マルチスレッド アプリケーションでは、後続の割り当てはすべて同期する必要があります。 CRT の依存関係を避けるには、/Zc:threadSafeInit-
フラグを使用してスレッド セーフな静的初期化の機能を無効にできます。
extern
extern
として宣言されたオブジェクトと変数は、別の翻訳単位または外側のスコープで定義されているオブジェクトを、外部リンケージを持つものとして宣言します。 詳細については、extern
と、翻訳単位とリンケージを参照してください。
thread_local
(C++11)
thread_local
指定子で宣言された変数は、それが作成されたスレッドでのみアクセスできます。 変数は、スレッドが作成されるときに作成され、スレッドが破棄されるときに破棄されます。 各スレッドには、それ自体の変数のコピーがあります。 Windows では、thread_local
は Microsoft 固有の __declspec( thread )
属性と機能的に同等です。
thread_local float f = 42.0; // Global namespace. Not implicitly static.
struct S // cannot be applied to type definition
{
thread_local int i; // Illegal. The member must be static.
thread_local static char buf[10]; // OK
};
void DoSomething()
{
// Apply thread_local to a local variable.
// Implicitly "thread_local static S my_struct".
thread_local S my_struct;
}
thread_local
指定子に関する注意点:
DLL 内の動的に初期化されたスレッド ローカル変数は、呼び出し元のすべてのスレッドで正しく初期化されない場合があります。 詳細については、
thread
を参照してください。thread_local
指定子は、static
またはextern
と組み合わせることができます。thread_local
は、データの宣言と定義にのみ適用できます。thread_local
は、関数の宣言または定義では使用できません。thread_local
は、静的ストレージ存続期間のあるデータ項目にのみ指定できます。これには、グローバル データ オブジェクト (static
とextern
の両方)、ローカルな静的オブジェクト、クラスの静的データ メンバーが含まれます。thread_local
宣言されたローカル変数は、他のストレージ クラスが指定されなき場合は暗黙的に静的です。つまり、ブロック スコープのthread_local
はthread_local static
と同じです。宣言と定義が同じファイルと別々のファイルのどちらで発生する場合でも、スレッド ローカル オブジェクトの宣言と定義には
thread_local
を使用する必要があります。thread_local
変数をstd::launch::async
と使用することはお勧めしません。 詳細については、「<future>
関数」を参照してください。
この Windows では、thread_local
は __declspec(thread)
と機能的に同等です。ただし、*__declspec(thread)
* が型定義に適用できる点および C コードでは有効である点は除きます。 thread_local
は C++ 標準の一部であり、移植性がより高いため、できるだけ常にこれを使用してください。
登録
Visual Studio 2017 バージョン 15.3 以降 (/std:c++17
モード以降で使用可能): register
キーワードは、サポートされているストレージ クラスではなくなりました。 その使用により、診断が発生します。 キーワードは、将来使用するために標準で引き続き予約されています。
register int val; // warning C5033: 'register' is no longer a supported storage class
例: 自動的な初期化と静的な初期化
ローカル自動オブジェクトまたは変数が、制御フローが定義に到達するたびに初期化されます。 ローカル静的オブジェクトまたは変数が、最初に制御フローが定義に到達すると初期化されます。
オブジェクトの初期化と破棄をログに記録するクラスを定義し、I1
、I2
、および I3
の 3 つのオブジェクトを定義する、次の例を考えます。
// initialization_of_objects.cpp
// compile with: /EHsc
#include <iostream>
#include <string.h>
using namespace std;
// Define a class that logs initializations and destructions.
class InitDemo {
public:
InitDemo( const char *szWhat );
~InitDemo();
private:
char *szObjName;
size_t sizeofObjName;
};
// Constructor for class InitDemo
InitDemo::InitDemo( const char *szWhat ) :
szObjName(NULL), sizeofObjName(0) {
if ( szWhat != 0 && strlen( szWhat ) > 0 ) {
// Allocate storage for szObjName, then copy
// initializer szWhat into szObjName, using
// secured CRT functions.
sizeofObjName = strlen( szWhat ) + 1;
szObjName = new char[ sizeofObjName ];
strcpy_s( szObjName, sizeofObjName, szWhat );
cout << "Initializing: " << szObjName << "\n";
}
else {
szObjName = 0;
}
}
// Destructor for InitDemo
InitDemo::~InitDemo() {
if( szObjName != 0 ) {
cout << "Destroying: " << szObjName << "\n";
delete szObjName;
}
}
// Enter main function
int main() {
InitDemo I1( "Auto I1" ); {
cout << "In block.\n";
InitDemo I2( "Auto I2" );
static InitDemo I3( "Static I3" );
}
cout << "Exited block.\n";
}
Initializing: Auto I1
In block.
Initializing: Auto I2
Initializing: Static I3
Destroying: Auto I2
Exited block.
Destroying: Auto I1
Destroying: Static I3
この例では、オブジェクト I1
、I2
、I3
がいつどのように初期化され、いつ破棄されるかを示します。
プログラムについていくつか注意点があります。
最初に、
I1
とI2
が定義されているブロックが制御フローによって終了されると、これらは自動的に破棄されます。次に、C++ では、ブロックの先頭でオブジェクトや変数を宣言する必要はありません。 さらに、これらのオブジェクトは、制御フローが定義に到達した場合にのみ初期化されます (
I2
そしてI3
、そのような定義の例です。出力には、初期化された日時が正確に表示されます。最後に、
I3
などの静的ローカル変数では、プログラム実行中は値が保持されますが、プログラムが終了すると破棄されます。
関連項目
フィードバック
https://aka.ms/ContentUserFeedback」を参照してください。
以下は間もなく提供いたします。2024 年を通じて、コンテンツのフィードバック メカニズムとして GitHub の issue を段階的に廃止し、新しいフィードバック システムに置き換えます。 詳細については、「フィードバックの送信と表示