詳解c++ 靜態成員變數
阿新 • • 發佈:2020-09-10
類定義時的靜態成員只是宣告,靜態成員的定義和初始化要在類之外完成
C++的static
關鍵字可修飾類成員變數/方法,表示變數/方法不從屬於特定物件,而是屬於類的。仔細琢磨靜態成員變數,會發現其與C++的方式既相容也矛盾,具有特殊性。
先說相容的一面。·C/C++·有宣告和定義的說法:宣告給出簽名,定義給出具體實現。對型別而言,宣告不一定能知道其物件佔用空間大小,但根據定義肯定能確定記憶體佔用。說靜態成員與C++方式是相容的,因為其初始化方式與方法的定義一致。下面是一個例子:
// Foo.hpp namespace tlanyan { // 類宣告和定義 class Foo { private: // 宣告靜態成員 static int value; public: // 方法宣告 void increaseValue(); int getValue() const; }; } // Foo.cpp namespace tlanyan { // 定義靜態成員變數並初始化 int Foo::value = 0; // 類方法定義 void Foo::increaseValue() { ++ value; } int Foo::getValue() { return value; } }
相對於相容點,靜態成員變數更多展現出怪異的一面,以下是個人總結:
- 靜態成員不能在類中初始化;非靜態成員可直接初始化,靜態成員在類中只是宣告,所以不能直接初始化。輔以
const
的靜態成員可以直接初始化,但那是const
的能力而非static
所有; - 對靜態成員初始化,需要在類之外定義時再完成;
- 初始化時不受訪問修飾符限制;
private
型別的靜態成員可直接訪問並賦值; - 靜態成員初始化時可呼叫函式,並且可以直接呼叫所屬類的私有函式;
其中第4點比較重要。在不支援C++11的編譯器上,要完成靜態map成員,就不得不借助函式返回:
#include <map> // 類定義 class Foo { private: std::map<const char*,int> maps; ... } // 靜態成員初始化 std::map<const char*,int> Foo::maps = Foo::initMap(); // 或者使用全域性函式 std::map<const char*,int> Foo::maps = initMap();
C++11引入了統一初始化和lambda
表示式,初始化的寫法更為簡單:
// 統一初始化 std::map<const char*,int> Foo::maps { {"a",31},{"b",32} }; // lambda表示式方式 std::map<const char*,int> Foo::maps = [] { map<const char*,int> _map; _map.insert(map<const char*,int>::value_type("a",31)); _map.insert(map<const char*,32)); return _map; }();
靜態成員的這些異常行為很容易聯想到全域性變數,兩者有許多相通的地方:在程式啟動前完成初始化,在程式終止後銷燬;存放的地方都是靜態儲存區而非堆疊;通過名字空間操作符獲取值;在非函式塊內通過函式呼叫或者lambda表示式完成初始化…
雖然各種面向物件程式語言都有靜態變數,並且使用比例不低。但從面向物件的角度,靜態成員是另一種形式的全域性變數,其破壞了隔離和封裝,增加了類之間的耦合,讓測試變得更困難。實際程式設計中,應當慎用全域性變數,並收緊其訪問許可權。
所以本質上靜態成員也是全域性變數,只是歸屬到特定類的名下。
以上就是詳解c++ 靜態成員變數的詳細內容,更多關於c++ 靜態成員變數的資料請關注我們其它相關文章!