儲存類別

C++ 變數宣告內容中的儲存類別 是管理物件存留期、連結和記憶體位置的類型規範。 指定的物件只能有一個儲存類別。 除非使用 externstaticthread_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關鍵字可用於下列情況。

  1. 當您在檔案範圍(全域和/或命名空間範圍)宣告變數或函式時, static 關鍵字會指定變數或函式具有內部連結。 當您宣告變數時,變數會擁有靜態持續期間,且編譯器會將其初始化為 0 (除非您指定其他值)。

  2. 當您在函式中宣告變數時, static 關鍵字會指定變數在呼叫該函式之間保留其狀態。

  3. 當您在類別宣告中宣告資料成員時, static 關鍵字會指定該成員的一個複本會由類別的所有實例共用。 static資料成員必須在檔案範圍中定義。 您宣告為 const static 的整數資料成員可以有初始化運算式。

  4. 當您在類別宣告中宣告成員函式時, static 關鍵字會指定該函式是由類別的所有實例共用。 成員 static 函式無法存取實例成員,因為函式沒有隱含 this 指標。 若要存取實例成員,請使用實例指標或參考的參數宣告函式。

  5. 您無法將 的成員 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規範可以與 staticextern 結合。

  • 您只能套用 thread_local 至資料宣告和定義; thread_local 不能用於函數宣告或定義。

  • 您只能在具有靜態儲存持續時間的資料項目上指定 thread_local ,其中包括全域資料物件 ( staticextern )、本機靜態物件,以及類別的靜態資料成員。 如果沒有提供其他儲存類別,任何宣告 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

範例:自動與靜態初始化

區域自動物件或變數會在每次控制流程到達其定義時初始化。 區域靜態物件或變數會在控制流程第一次到達其定義時初始化。

請考慮下列範例,範例中會定義記錄物件初始化和解構的類別,然後再定義三個物件 I1I2I3

// 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

此範例示範 物件 、 I2I3 的初始化方式和時機 I1 ,以及它們終結時的方式和時機。

程式有數點需要注意:

  • 首先,當控制流程結束定義區塊時, I1 會自動終結 和 I2

  • 其次,在 C++ 中,不需要在區塊開頭宣告物件或變數。 再者,只有在控制流程到達其定義時,這些物件才會初始化 ( I2I3 是這類定義的範例。輸出會在初始化時顯示。

  • 最後,靜態區域變數,例如 I3 在程式執行時保留其值,但在程式終止時會終結。

另請參閱

宣告和定義