const 常量的在超出其作用域的時候會被釋放,但是 static 靜態變量在其作用域之外並沒有釋放,只是不能訪問。
static 修飾的是靜態變量,靜態函數。對於類來說,靜態成員和靜態函數是屬於整個類的,而不是屬於對象。可以通過類名來訪問,但是其作用域限制於包含它的文件中。
static 變量在類內部聲明,但是必須在類的外部進行定義和初始化。
const 常量在類內部聲明,但是定義只能在構造函數的初始化列表進行。
class A { public: A(int a) : constNum(a) {} private: static int staticNum; const int constNum; }; int A::staticNum = 100;
從上面的代碼可以看出,const 常量的不變形只是針對與一個對象來說的,同一個類的不同對象的 const 常量的值可以不一樣。
如果想讓 const 常量在類的所有實例對象的值都一樣,可以用 static const (const static),使用方式如下:
1 class A { 2 const static int num1; // 聲明 3 const static int num2 = 13; // 聲明和初始化 4 }; 5 const int A::num1 = 12; // 定義並初始化 6 const int num2; // 定義
上面兩種方式都可以對 const static 常量進行初始化。注意,第 3 行的代碼並沒有對 num2 進行定義,它只是進行聲明。其實這里給了值 13 也沒用進行初始化,因為變量必須在定義了以后才進行初始化。但是我們會發現很奇怪的問題,如下:
1 class A { 2 public: 3 const static int num2 = 13; // 聲明和【初始化】 4 }; 5 6 int main(int argc, char const *argv[]) 7 { 8 cout << A::num2 << endl; 9 return 0; 10 }
上面代碼的執行結果是 13,也就是說,num2 還沒有定義就可以使用了。至於 num2 只是聲明沒用定義的證明如下:
1 class A { 2 public: 3 const static int num2 = 13; // 聲明和【初始化】 4 }; 5 // const int A::num2; 6 7 int main(int argc, char const *argv[]) 8 { 9 cout << &(A::num2) << endl; 10 return 0; 11 }
在將第 5 行注釋后,編譯結果如下:
Undefined symbols for architecture x86_64: "A::num2", referenced from: _main in a-1e0f08.o ld: symbol(s) not found for architecture x86_64 clang: error: linker command failed with exit code 1 (use -v to see invocation) [Finished in 0.3s with exit code 1]
也就是說 num2 還沒有定義。在取消注釋后成功輸出 num2 的地址。說明在 加上 const int A::num2; 后 num2 才被定義。
那為什么 num2 還沒定義就可以使用了呢,其實因為 num2 是 const 常量,在生成匯編代碼的時候並不是在 num2 的地址內取值,而是直接將 num2 【初始化】的時候的那個值替換掉 num2。這也就是用指針改變 const 常量的值的時候 const 常量的字面值並沒有變化的原因。這個可以自己去看程序的匯編代碼來證明。這個可能在不同的編譯器有不同的實現,因為c++標准並沒有規定 const 要怎樣實現,不同的編譯器的實現可能不一樣。
另外一個要注意的地方是,在類內部進行 static const 的初始化只能針對於內置類型,比如如下是會報錯的:
class A { public: const static string str = "str"; }; const string str;
所以如果不是必要,一般都是采用類外初始化的形式。那么什么情況下是必要的呢?我們看如下代碼:
//MyClass.h class MyClass{ public: static const int MyArraySize = 256; private: int MyArray[MyArraySize]; };
上面這樣是沒問題的,但是下面這樣就會報錯:
1 //MyClass.h 2 3 class MyClass{ 4 public: 5 static const int MyArraySize; 6 static const int MyValue; 7 8 private: 9 int MyArray[MyArraySize]; 10 }; 11 12 //MyClass.cpp 13 #include "MyClass.h" 14 15 const int MyClass::MyArraySize = 256; 16 const int MyClass::MyValue = 100;
在第 9 行,如果 MyArraySize 有初始化的話,會直接用它的值代替。但是這里找不到它的值,所以無法作為數組定義的size。這個時候用前面的方法就會好一點。
最后一個要注意的是,類內的 static const 常量的【初始化】必須用常量表達式,也就是說,這里的【初始化】值必須是一個能直接使用的值。所以如果此時要用函數返回值的話,函數應該是 constexpr 的,如下:
constexpr int fun() { return 12; } class A { public: const static int num = fun(); }; const int A::num;
當然可以在 fun 函數里面進行一些計算操作。
