靜態類成員
一個類也允許有 static
成員,可以是變數或函式。這些被認為屬於類範圍,但不被視為普通會員; 它們具有靜態儲存持續時間(它們從程式的開始到結束),不依賴於類的特定例項,並且整個類只存在一個副本。
class Example {
static int num_instances; // Static data member (static member variable).
int i; // Non-static member variable.
public:
static std::string static_str; // Static data member (static member variable).
static int static_func(); // Static member function.
// Non-static member functions can modify static member variables.
Example() { ++num_instances; }
void set_str(const std::string& str);
};
int Example::num_instances;
std::string Example::static_str = "Hello.";
// ...
Example one, two, three;
// Each Example has its own "i", such that:
// (&one.i != &two.i)
// (&one.i != &three.i)
// (&two.i != &three.i).
// All three Examples share "num_instances", such that:
// (&one.num_instances == &two.num_instances)
// (&one.num_instances == &three.num_instances)
// (&two.num_instances == &three.num_instances)
靜態成員變數不被認為是在類中定義的,只是宣告的,因此它們的定義在類定義之外; 允許程式設計師(但不是必需的)在其定義中初始化靜態變數。定義成員變數時,省略關鍵字 static
。
class Example {
static int num_instances; // Declaration.
public:
static std::string static_str; // Declaration.
// ...
};
int Example::num_instances; // Definition. Zero-initialised.
std::string Example::static_str = "Hello."; // Definition.
因此,靜態變數可以是不完整的型別(除了 void
),只要它們稍後被定義為完整型別。
struct ForwardDeclared;
class ExIncomplete {
static ForwardDeclared fd;
static ExIncomplete i_contain_myself;
static int an_array[];
};
struct ForwardDeclared {};
ForwardDeclared ExIncomplete::fd;
ExIncomplete ExIncomplete::i_contain_myself;
int ExIncomplete::an_array[5];
靜態成員函式可以在類定義的內部或外部定義,與普通成員函式一樣。與靜態成員變數一樣,在類定義之外定義靜態成員函式時,省略關鍵字 static
。
// For Example above, either...
class Example {
// ...
public:
static int static_func() { return num_instances; }
// ...
void set_str(const std::string& str) { static_str = str; }
};
// Or...
class Example { /* ... */ };
int Example::static_func() { return num_instances; }
void Example::set_str(const std::string& str) { static_str = str; }
如果靜態成員變數宣告為 const
但不是 volatile
,並且是一個整數或列舉型別,則可以在類定義中的宣告處初始化它。
enum E { VAL = 5 };
struct ExConst {
const static int ci = 5; // Good.
static const E ce = VAL; // Good.
const static double cd = 5; // Error.
static const volatile int cvi = 5; // Error.
const static double good_cd;
static const volatile int good_cvi;
};
const double ExConst::good_cd = 5; // Good.
const volatile int ExConst::good_cvi = 5; // Good.
Version >= C++ 11
從 C++ 11 開始,LiteralType
型別的靜態成員變數(根據 constexpr
規則可以在編譯時構造的型別)也可以宣告為 constexpr
; 如果是這樣,它們必須在類定義中初始化。
struct ExConstexpr {
constexpr static int ci = 5; // Good.
static constexpr double cd = 5; // Good.
constexpr static int carr[] = { 1, 1, 2 }; // Good.
static constexpr ConstexprConstructibleClass c{}; // Good.
constexpr static int bad_ci; // Error.
};
constexpr int ExConstexpr::bad_ci = 5; // Still an error.
如果 const
或 constexpr
靜態成員變數使用了 odr (非正式地,如果它的地址已被採用或被分配給引用),則它必須在類定義之外具有單獨的定義。此定義不允許包含初始化程式。
struct ExODR {
static const int odr_used = 5;
};
// const int ExODR::odr_used;
const int* odr_user = & ExODR::odr_used; // Error; uncomment above line to resolve.
由於靜態成員不依賴於給定例項,因此可以使用範圍運算子::
訪問它們。
std::string str = Example::static_str;
也可以像訪問普通的非靜態成員一樣訪問它們。這具有歷史意義,但是比範圍運算子更少使用,以防止對成員是靜態還是非靜態的混淆。
Example ex;
std::string rts = ex.static_str;
與非靜態類成員一樣,類成員可以訪問靜態成員而無需限定其作用域。
class ExTwo {
static int num_instances;
int my_num;
public:
ExTwo() : my_num(num_instances++) {}
static int get_total_instances() { return num_instances; }
int get_instance_number() const { return my_num; }
};
int ExTwo::num_instances;
它們不可能是天籟 13,也不是必須的; 因為它們不依賴於任何給定的例項,例項是否為 const 不會影響靜態成員。
struct ExDontNeedMutable {
int immuta;
mutable int muta;
static int i;
ExDontNeedMutable() : immuta(-5), muta(-5) {}
};
int ExDontNeedMutable::i;
// ...
const ExDontNeedMutable dnm;
dnm.immuta = 5; // Error: Can't modify read-only object.
dnm.muta = 5; // Good. Mutable fields of const objects can be written.
dnm.i = 5; // Good. Static members can be written regardless of an instance's const-ness.
靜態成員尊重訪問修飾符,就像非靜態成員一樣。
class ExAccess {
static int prv_int;
protected:
static int pro_int;
public:
static int pub_int;
};
int ExAccess::prv_int;
int ExAccess::pro_int;
int ExAccess::pub_int;
// ...
int x1 = ExAccess::prv_int; // Error: int ExAccess::prv_int is private.
int x2 = ExAccess::pro_int; // Error: int ExAccess::pro_int is protected.
int x3 = ExAccess::pub_int; // Good.
因為它們不依賴於給定的例項,所以靜態成員函式沒有 this
指標; 因此,除非傳遞例項,否則它們無法訪問非靜態成員變數。
class ExInstanceRequired {
int i;
public:
ExInstanceRequired() : i(0) {}
static void bad_mutate() { ++i *= 5; } // Error.
static void good_mutate(ExInstanceRequired& e) { ++e.i *= 5; } // Good.
};
由於沒有 this
指標,它們的地址不能儲存在指向成員函式的指標中,而是儲存在普通的指向函式的指標中。
struct ExPointer {
void nsfunc() {}
static void sfunc() {}
};
typedef void (ExPointer::* mem_f_ptr)();
typedef void (*f_ptr)();
mem_f_ptr p_sf = &ExPointer::sfunc; // Error.
f_ptr p_sf = &ExPointer::sfunc; // Good.
由於沒有 this
指標,它們也不能是 const
或 volatile
,也不能有 ref-qualifiers。他們也不可能是虛擬的。
struct ExCVQualifiersAndVirtual {
static void func() {} // Good.
static void cfunc() const {} // Error.
static void vfunc() volatile {} // Error.
static void cvfunc() const volatile {} // Error.
static void rfunc() & {} // Error.
static void rvfunc() && {} // Error.
virtual static void vsfunc() {} // Error.
static virtual void svfunc() {} // Error.
};
由於它們不依賴於給定的例項,因此靜態成員變數被有效地視為特殊的全域性變數; 它們是在程式啟動時建立的,並在退出時被銷燬,無論該類的例項是否存在。只存在每個靜態成員變數的一個副本(除非該變數被宣告為 thread_local
(C++ 11 或更高版本),在這種情況下,每個執行緒有一個副本)。
靜態成員變數與類具有相同的連結,無論該類具有外部連結還是內部連結。本地類和未命名的類不允許具有靜態成員。