儲存類別
C++ 變數宣告內容中的儲存類別 是管理物件存留期、連結和記憶體位置的類型規範。 指定的物件只能有一個儲存類別。 除非使用 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
關鍵字會指定該成員的一個複本會由類別的所有實例共用。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
區域變數初始化保證為安全線程。 此功能有時稱為 magic 靜態 。 不過,在多執行緒應用程式中,必須同步處理所有後續指派。 您可以使用 旗標來停用 /Zc:threadSafeInit-
安全線程靜態初始化功能,以避免對 CRT 產生相依性。
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
。我們不建議您搭配
std::launch::async
使用thread_local
變數。 如需詳細資訊,請參閱<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
:
// 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
此範例示範 物件 、 I2
和 I3
的初始化方式和時機 I1
,以及它們終結時的方式和時機。
程式有數點需要注意:
首先,當控制流程結束定義區塊時,
I1
會自動終結 和I2
。其次,在 C++ 中,不需要在區塊開頭宣告物件或變數。 再者,只有在控制流程到達其定義時,這些物件才會初始化 (
I2
和I3
是這類定義的範例。輸出會在初始化時顯示。最後,靜態區域變數,例如
I3
在程式執行時保留其值,但在程式終止時會終結。