一、static关键字的作用
- 主要有两方面的作用:改变生命周期,和改变链接属性的作用域
二、static关键字四个使用场景
1、局部静态变量(在函数内部)
-
**本质:**变量的生命周期从栈变成全局静态存储区,但作用域仍局限于函数内部
-
**特点:**局部静态变量只能被初始化一次,之后函数调用结束该变量也不会被销毁,下次调用该函数,获取的还是上次的值
-
**线程安全:**从C++11起,局部静态变量的初始化是线程安全的。因为底层编译器会自动加锁,这也是单例模式的底层实现原理
-
代码实现:
cpp
void counter() {
static int count = 0; // 只初始化一次,线程安全(C++11起)
count++;
cout << count << endl;
}
// 调用3次输出:1, 2, 3
2、全局静态变量/函数(文件作用域)
-
**本质:**全局静态变量和函数在当前文件中都可以调用,而其他cpp文件无法调用该变量或函数。从外部链接变为内部链接。
-
**作用:**只在本编译单元(.cpp文件)中可见,其他文件即使使用extern声明也无法访问。
-
可用来隐藏实现细节,防止多文件编译时的重名冲突
-
比起全局静态,C++更推荐使用匿名的namespace来替换
-
代码:
cpp
// FileA.cpp
static int s_hidden = 100; // 仅 FileA.cpp 可见
static void helper() {} // 仅 FileA.cpp 可见
// FileB.cpp
extern int s_hidden; // ❌ 链接错误,找不到该符号
3、静态成员变量(类内部)
-
**本质:**属于类本身,不属于某个对象,所有该类的对象共享同一份内存
-
在C++17之前,静态成员变量必须在类外单独定义并初始化,否则会链接报错。因为一个头文件中的静态成员变量会被多个文件引用,分不清到底是哪个类的静态变量。
-
为解决上面问题,在C++17中引入 inline static允许在类内部进行初始化,而无需去cpp文件中初始化
-
代码:
cpp
class App {
public:
static int version; // 声明(未定义)
inline static int build = 42; // C++17起:直接定义并初始化,无需类外实现
};
// C++17之前必须在 .cpp 文件补一行:
// int App::version = 1;
4、静态成员函数(类内部)
-
本质:没有this指针,无法访问普通成员变量与函数,只能访问静态成员
-
调用方式: 通过类名::函数() ,无需创建对象,就可以调用静态成员函数
-
静态成员函数不能是const静态函数,与virtual虚函数。因为const函数修饰this指针,而静态函数没有this。
-
而virtual依赖于虚表(vptr)和对象,静态函数不依赖对象
-
代码:
cpp
class Math {
public:
static double square(double x) { return x * x; }
};
double d = Math::square(5.0); // 直接调用
三、总结表格
|---------|-----------------|---------------|--------------|
| 场景 | 存储位置/生命周期 | 作用域/链接性 | 初始化次数 |
| 局部变量 | 静态存储区(程序结束时才销毁) | 函数内部/作用域为函数范围 | 1次(线程安全) |
| 全局变量/函数 | 静态存储区 | 本文件内部(内部链接) | 1次(程序启动时初始化) |
| 静态成员函数 | 静态存储区(全局共享) | 类域(需外部定义) | 1次 |
| 静态成员函数 | 无this,属于类 | 类域(通过类名调用) | -- |
四、static与thread_local变量区别
-
static变量:所有线程共享一份
-
thread_local修饰的变量,每个线程独享一份