静态类成员
一个类也允许有 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 或更高版本),在这种情况下,每个线程有一个副本)。
静态成员变量与类具有相同的链接,无论该类具有外部链接还是内部链接。本地类和未命名的类不允许具有静态成员。