静态成员
更新时间:2026-08-27。本文是
languages/cpp/主题入门层第 22 篇。static成员不属于任何具体对象,属于"类本身":所有对象共享同一份静态变量,静态成员函数没有this、不绑定对象。它是 C 里"全局变量 + 文件作用域函数"的正规军版。
本文要回答的问题
- 静态成员变量和普通成员变量差在哪?
- 静态成员函数为什么没有
this? - 静态成员在什么场景派得上用场?
一、静态成员变量:类的属性,不是对象的属性
class Counter {
static int total_; // 静态成员变量:声明
int id_;
public:
Counter() : id_(++total_) {} // 每个对象拿一个递增的编号
static int total() { return total_; } // 静态成员函数
};
int Counter::total_ = 0; // 定义 + 初始化(C++17 前必须写)static int total_ 是"类的变量"——它不属于任何一个对象,全类共享一份。Counter a, b, c; 三个对象的 id_ 各不相同,但 total_ 只有一份,从 0 开始涨。典型场景:计数(这里统计创建了多少个对象)、共享配置、单例。
声明和定义分离:static 成员变量在类里只是声明,还得在类外定义一次(int Counter::total_ = 0;)。不定义就链接报错——这是新手常卡的地方。C++17 起可以偷懒:类内写 inline static int total_ = 0;,一步到位。
二、静态成员函数:没有 this 的函数
静态成员函数前面也带 static,特点是不绑定对象、没有 this。因此:
- 它不能访问普通成员变量/函数(那些要经过
this才能定位); - 它只能访问静态成员(静态变量、其他静态函数);
- 它可以用类名直接调用,不用创建对象:
int n = Counter::total(); // 直接用类名调,不需要对象
// Counter c; int n = c.total(); // 也能用对象调,但没必要对照上一篇文章的 this:普通成员函数 = "对象的操作",静态成员函数 = "类的操作"。后者更像 C 里的普通函数,只是收编进了类的命名空间。
三、应用场景
| 场景 | 例子 |
|---|---|
| 全局计数 | 创建的对象总数、生成的 ID 序号 |
| 共享配置 | 所有对象共用的阈值、默认值 |
| 工具函数 | Math::sqrt 式的纯计算(不碰对象状态) |
| 单例模式 | 全局唯一实例的获取入口 |
| 工厂方法 | 创建对象的入口(返回 std::unique_ptr) |
和 C 的对应关系很直观:静态成员变量 ≈ 全局变量(但名字收进类里、受访问控制约束),静态成员函数 ≈ 全局函数(但归入类的命名空间)。好处是名字不污染全局、还能用 private 把它藏起来——"只有类自己碰得到的全局变量"。
class AppConfig {
static inline int timeout_ = 30; // C++17:类内定义
public:
static void setTimeout(int t) {
if (t > 0) timeout_ = t; // 校验还是能做
}
static int timeout() { return timeout_; }
};timeout_ 全程序只有一份,读写都走 AppConfig:: 接口——C 的"全局配置变量"在 C++ 里就是这么体面地收编。
四、和 C 对比
| 对比项 | C | C++ |
|---|---|---|
| 全局变量 | 裸全局(名字冲突风险) | static 成员,收进类 |
| 全局函数 | 裸函数 | static 成员函数 |
| 访问控制 | 无 | private 静态成员藏得严实 |
| 计数/单例 | 手搓全局 + 手动初始化 | 类内声明 + 类外定义 |
| 初始化时机 | 手动 | C++17 inline static 类内搞定 |
C 里写计数器就是 int global_count = 0; 放在文件顶——所有文件都能看见它、改它,冲突和污染是迟早的事。C++ 的静态成员把它收进类、可设 private、可通过静态函数校验访问,同样是"全局"的用途,纪律性完全两样。
五、与本站主线衔接
- 静态成员与全局变量的内存位置(数据段)见 L5 ELF 与内存布局;
- 单例模式与静态成员的配合见高手层设计模式篇;
- 下一篇是类与对象的收尾:友元——打破
private的例外口子。
一句话总结
静态成员变量属于类、不属于对象,全类共享一份(类内声明 + 类外定义,C++17 可用 inline static 一步到位);静态成员函数没有 this、不绑定对象,只能访问静态成员,用 类名::函数() 直接调用;它是 C 全局变量/全局函数的正规军版——名字收进类、访问受控、还能私有化。