刚开始学 C++ 的时候,我一直以为 static 很简单——不就是“静态”的意思吗?类里可以定义静态成员变量,函数里也可以写静态变量,看起来好像都差不多。
但事实上,static 这个关键字很容易把人绕进去。它在不同位置出现时,含义完全不一样:有时候影响变量生命周期,有时候限制作用域,有时候又和类对象无关。这篇文章不按概念硬背,而是按 static 出现的位置,把 C++ 里的几种用法拆开讲清楚。
第一种:函数内的 static 局部变量

当你在函数内部写 static int count = 0; 时,这个变量不再是普通的局部变量。普通局部变量在函数调用结束时就被撤销了,下次调用再重新初始化。而 static 局部变量会一直活在内存里,生命周期从程序启动到程序结束。它只会在第一次执行到这行代码时初始化一次。
void visit() {
static int count = 0;
count++;
std::cout << count << std::endl;
}
int main() {
visit(); // 输出 1
visit(); // 输出 2
visit(); // 输出 3
}
这个特性很实用,比如实现单例模式时就会用到。但注意,虽然它是静态的,作用域仍然只限于这个函数内部,外面的代码看不见它。
第二种:文件作用域的 static

当 static 用在全局变量或函数前面时,意思又变了。它表示这个符号只在当前文件内可见,其他源文件无法访问。
// file.cpp
static int internal_counter = 0; // 只在 file.cpp 里可见
static void helper() { // 只在 file.cpp 里可见
// ...
}
这种做法在 C 语言里很常见,用来隐藏模块内部的实现细节。但在 C++ 里,更推荐的做法是用匿名命名空间:
namespace {
int internal_counter = 0;
void helper() { }
}
效果和 static 一模一样,但更符合 C++ 的设计约定。我曾经在一个项目里见过同事在头文件里写了 static int g_flag;,然后在多个源文件里分别包含这个头文件,结果每个文件都有自己独立的 g_flag,数据完全不通,调了三天才发现。
第三种:类中的 static 成员变量

这是面试里最常考的。类里的 static 成员变量不属于任何一个对象,而是属于类本身。所有对象共享同一份数据。
class Widget {
public:
static int count; // 声明
Widget() { count++; }
};
int Widget::count = 0; // 必须在类外定义
int main() {
Widget a;
Widget b;
std::cout << Widget::count; // 输出 2
}
关键细节在于,类里的声明只是声明,真正的定义和初始化必须在类外写一次。如果忘了这一行,编译时会报链接错误。我刚学 C++ 的时候,在这里被编译器教育了好几次。
第四种:类中的 static 成员函数
static 成员函数也是属于类的,不属于任何对象。它们可以直接通过类名调用,不需要创建对象。
class Math {
public:
static int add(int a, int b) {
return a + b;
}
};
int main() {
int result = Math::add(3, 4); // 直接调用,不需对象
}
重要的是,static 成员函数里面没有 this 指针,所以它不能访问非静态成员变量或非静态成员函数。如果你在 static 函数里写了 this->value,编译器会直接报错。
第五种:C++11 的线程安全静态初始化
这一点很多人不知道。在 C++11 之前,函数内的 static 变量初始化不是线程安全的。如果多个线程同时第一次进入这个函数,可能会出现竞态条件。
但从 C++11 开始,函数内部的静态局部变量的初始化被标准保证为线程安全的。编译器会自动插入锁机制,确保只有一个线程去执行初始化。所以你可以放心地用 static 局部变量来实现线程安全的懒汉单例:
Singleton& getInstance() {
static Singleton instance; // C++11 起线程安全
return instance;
}
这是 Scott Meyers 推荐的写法,比双检锁更简洁、更安全。
一张表看懂所有用法

最后用一张表把几种用法对比清楚:
| 用法 |
作用域 |
生命周期 |
链接性 |
关键记忆点 |
| 函数内局部变量 |
函数内 |
程序结束 |
无 |
只初始化一次 |
| 文件全局变量 |
文件内 |
程序结束 |
内部链接 |
隐藏实现细节 |
| 类静态成员变量 |
类范围 |
程序结束 |
外部链接 |
必须在类外定义 |
| 类静态成员函数 |
类范围 |
无 |
外部链接 |
没有 this 指针 |
写到这里又想起曾经的一次面试。如果当时我能这么一条一条说出来,可能就过了。但后来我想了想,面试官也不是在考我背书,而是在看我是不是真的理解这些概念。所以这些并不是面试八股文,而是帮你写出正确代码的基础。你在函数里写了 static,它是什么意思?你在类里写了 static,它又是什么意思?每一次你用这个关键词的时候,你应该清楚它的行为。
这就是 C++ 里 static 的全部故事。没有什么魔法,就是几个完全不同的概念被塞进了同一个关键词里。理解了,就不会再被它难倒了。
关于 C++ 的更多底层机制与工程实践,云栈社区上还有大量深入的文章和讨论,欢迎一起交流。