static
members
在类定义内部,关键字 static 用于声明不绑定到类实例的成员。
在类定义之外,它具有不同的含义:参见 存储期 。
目录 |
语法
静态成员的声明是一种 成员声明 ,其声明说明符包含关键字 static 。关键字 static 通常出现在其他说明符之前(这就是为什么其语法常被非正式地描述为 static data-member 或 static member-function ),但也可以出现在说明符序列中的任意位置。
任何静态数据成员和静态成员函数的名称必须不同于其所属类的名称。
说明
类的静态成员不与类的对象相关联:它们是具有静态 或线程 (since C++11) 存储期 的独立变量或常规函数。
static 关键字仅用于类定义内部的静态成员声明,而不用于该静态成员的定义:
class X { static int n; }; // 声明(使用 'static') int X::n = 1; // 定义(不使用 'static')
类体内的声明不是定义,且可以将成员声明为 不完整类型 (除 void 外),包括声明该成员的类类型本身:
struct Foo; struct S { static int a[]; // 声明,不完整类型 static Foo x; // 声明,不完整类型 static S s; // 声明,不完整类型(在其自身定义内) }; int S::a[10]; // 定义,完整类型 struct Foo {}; Foo S::x; // 定义,完整类型 S S::s; // 定义,完整类型
| (C++11 起) |
要引用类
T
的静态成员
m
,可使用两种形式:限定名称
T::m
或成员访问表达式
E.m
或
E->m
,其中
E
分别是计算结果为
T
或
T*
的表达式。当处于同一类作用域内时,无需使用限定:
struct X { static void f(); // 声明 static int n; // 声明 }; X g() { return X(); } // 返回 X 的某个函数 void f() { X::f(); // X::f 是静态成员函数的限定名称 g().f(); // g().f 是引用静态成员函数的成员访问表达式 } int X::n = 7; // 定义 void X::f() // 定义 { n = 1; // 在此作用域中可直接以 n 访问 X::n }
静态成员遵循 类成员访问规则(private、protected、public) 。
静态成员函数
静态成员函数不与任何对象关联。当被调用时,它们没有 this 指针。
静态成员函数不能是 virtual 、 const 、 volatile 或 引用限定 的。
静态成员函数的地址可以存储在常规的 函数指针 中,但不能存储在 成员函数指针 中。
静态数据成员
静态数据成员不与任何对象关联。即使没有定义该类的任何对象,它们依然存在。在整个程序中静态数据成员只有一个实例,具有静态 存储期 ,除非使用关键字 thread_local ,此时每个线程拥有一个具有线程存储期的该对象 (C++11 起) 。
静态数据成员不能是 mutable 。
命名空间作用域中的类静态数据成员若其所属类具有外部链接(即不属于 无名命名空间 的成员),则这些静态数据成员具有 外部链接 。局部类(在函数内部定义的类)和无名类(包括无名类的成员类)不能拥有静态数据成员。
|
静态数据成员可被声明为 inline 。内联静态数据成员可以在类定义中定义,并可指定初始化器。它不需要类外定义: struct X { inline static int fully_usable = 1; // 无需类外定义,可ODR使用 inline static const std::string class_name{"X"}; // 同理 static const int non_addressable = 1; // 对比非内联常量,可取其值使用 // 但不可ODR使用 // static const std::string class_name{"X"}; // 此形式的非整型声明完全不允许 // }; |
(since C++17) |
常量静态成员
如果整型或枚举类型的静态数据成员被声明为 const (且非 volatile ),则可以直接在类定义内部使用 初始化器 进行初始化,其中每个表达式都是 常量表达式 :
struct X { const static int n = 1; const static int m{2}; // 自 C++11 起 const static int k; }; const int X::k = 3;
|
若 字面类型 的静态数据成员被声明为 constexpr ,则必须在类定义内部直接使用每个表达式均为常量表达式的初始化器进行初始化: struct X { constexpr static int arr[] = { 1, 2, 3 }; // OK constexpr static std::complex<double> n = {1,2}; // OK constexpr static int k; // Error: constexpr static requires an initializer }; |
(since C++11) |
如果 const 非内联 (C++17 起) 静态数据成员 或 constexpr 静态数据成员 (C++11 起) (C++17 前) 被 ODR 使用 ,仍需要在命名空间作用域提供定义,但该定义不能包含初始化器。
|
一个 constexpr 静态数据成员会隐式成为 inline ,且无需在命名空间作用域中重新声明。这种不带初始化器的重新声明(过去是必需的)仍然被允许,但已被弃用。 |
(since C++17) |
struct X { static const int n = 1; static constexpr int m = 4; }; const int *p = &X::n, *q = &X::m; // X::n 和 X::m 被 ODR 使用 const int X::n; // … 因此需要提供定义 constexpr int X::m; // …(C++17 中 X::m 除外)
关键词
缺陷报告
以下行为变更缺陷报告被追溯应用于先前发布的C++标准。
| 缺陷报告 | 适用标准 | 发布时行为 | 正确行为 |
|---|---|---|---|
| CWG 194 | C++98 | (静态)成员函数名称可以与类名相同 |
增加了命名限制(包括
非静态成员函数 ) |
参考文献
- C++23 标准 (ISO/IEC 14882:2024):
-
- 11.4.9 静态成员 [class.static]
- C++20 标准 (ISO/IEC 14882:2020):
-
- 11.4.8 静态成员 [class.static]
- C++17 标准 (ISO/IEC 14882:2017):
-
- 12.2.3 静态成员 [class.static]
- C++14 标准 (ISO/IEC 14882:2014):
-
- 9.4 静态成员 [class.static]
- C++11 标准 (ISO/IEC 14882:2011):
-
- 9.4 静态成员 [class.static]
- C++98 标准 (ISO/IEC 14882:1998):
-
- 9.4 静态成员 [class.static]