眾所周知,將一個類內的某個成員變量聲明為static型,可以使得該類實例化得到的對象實現對象間數據共享。
在C++中,通常將一個類的聲明寫在頭文件中,將這個類的具體定義(實現)寫在cpp源文件中。
因此,就引出了static成員變量的聲明與定義問題:
1. 如果一個類內成員變量是static的,且需要將之設定為常量(const),那么這個變量聲明與初始化均可寫在頭文件內。
舉個例子:
1 // Scanner.hpp 2 class Scanner { 3 public: 4 const static int MAX_SIZE = 0xFFFF; 5 ... 6 };
這里直接將MAX_SIZE聲明與定義寫在了頭文件。這很好理解,編譯器在為這個類分配內存空間的時候,已經知道了這個類內變量無需為每個對象“拷貝”一份(因為它是static的),並且又知道了它在程序運行過程中的值保持不變(因為它是const的),那么就可以直接將其處理。因此頭文件內的類聲明實現信息已經足夠,從而這樣的寫法是合理的。
需要注意的是,不能在頭文件內聲明const static成員變量,而在具體源cpp文件內實現其初始化。因為這樣編譯器需要根據具體的實現文件來確定該成員變量的初始值,若實際應用中沒有相應的實現文件(源cpp文件)來對這個成員變量實現初始化,則編譯器無法明確意圖,從而無法完成編譯。
2.如果一個類內成員變量是static的,但不需要將其設定為常量(const),那么這個變量聲明於頭文件內,初始化(定義/實現)寫在對應的cpp源文件中。
舉個例子:
1 // Scanner.hpp 2 class Scanner { 3 public: 4 static int line; 5 ... 6 };
// Scanner.cpp #include "Scanner.hpp" int Scanner::line = 1; ...
這樣的實現方式給了類實現者一種相對的自由。這樣就可以針對不同的實現文件實現不同的類內static字段初始化。
比如有兩個Scanner實現文件: Scanner1.cpp 和 Scanner2.cpp,那么這兩個源文件分別#include "Scanner.hpp",就可以分別實現各自的類內line值初始化。當然,在實際應用時,切不可同時使用Scanner1和Scanner2,因為這樣會發生符號表重定義沖突(因為兩個cpp文件均實現了Scanner,符號表內重復填充屬性字段)。
需要注意的是,此時不能將上述line的初始化寫在頭文件(Scanner.hpp)里。道理類似,因為編譯器發現這個字段並不是const的,也就是說這個字段可以被不同的實現文件(cpp文件)來具體確定其初始值,那么編譯器就不負責在聲明階段對其實現初始化,因此在頭文件內初始化一個類內非const的static成員變量是非法的。