1. 程式人生 > 程式設計 >詳解c++ 靜態成員變數

詳解c++ 靜態成員變數

類定義時的靜態成員只是宣告,靜態成員的定義和初始化要在類之外完成

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;
 }
}

相對於相容點,靜態成員變數更多展現出怪異的一面,以下是個人總結:

  1. 靜態成員不能在類中初始化;非靜態成員可直接初始化,靜態成員在類中只是宣告,所以不能直接初始化。輔以const的靜態成員可以直接初始化,但那是const的能力而非static所有;
  2. 對靜態成員初始化,需要在類之外定義時再完成;
  3. 初始化時不受訪問修飾符限制;private型別的靜態成員可直接訪問並賦值;
  4. 靜態成員初始化時可呼叫函式,並且可以直接呼叫所屬類的私有函式;

其中第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++ 靜態成員變數的資料請關注我們其它相關文章!