靜態類成員

一個類也允許有 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.

如果 constconstexpr 靜態成員變數使用了 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 指標,它們也不能是 constvolatile,也不能有 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 或更高版本),在這種情況下,每個執行緒有一個副本)。

靜態成員變數與類具有相同的連結,無論該類具有外部連結還是內部連結。本地類和未命名的類不允許具有靜態成員。