在 C++ 中,存储类(Storage Class)决定了变量或函数的范围(Scope)、生命周期(Lifetime)、可见性(Visibility)和存储位置(Storage Location)。理解存储类对于编写高效和正确的 C++ 代码至关重要。
1. 常见存储类及其定义
C++ 提供了以下几种存储类:
-
auto(自动存储类):
- 范围:局部变量(函数内)
- 生命周期:当程序执行到该变量的声明时分配内存,并在离开变量作用域时释放。
- 使用注意:C++11 之后,
auto
主要用于自动类型推导,而不是存储类说明符。
-
register(寄存器存储类):
- 范围:局部变量
- 生命周期:与
auto
类似。 - 存储位置:尽量存储在 CPU 寄存器中以提高访问速度,但并不保证一定会在寄存器中存储。
- 使用注意:现代编译器一般会自动优化寄存器分配,因此
register
现在很少使用。
-
static(静态存储类):
- 范围:局部变量(函数内)或全局变量(文件范围内)
- 生命周期:在程序的整个生命周期内保持不变。即使在函数内定义的静态变量也是如此,它们在第一次初始化后将一直存在。
- 使用注意:静态局部变量只会被初始化一次,且在函数调用结束后仍保持其值。静态全局变量(文件范围内的变量)只能在定义它的文件中可见。
-
extern(外部存储类):
- 范围:全局变量(或函数)
- 生命周期:与程序生命周期一致。
- 使用注意:
extern
用于声明变量或函数的引用,而不是定义。通常在一个文件中声明,在另一个文件中定义,用于多文件程序的变量或函数共享。
-
mutable(可变存储类):
- 范围:类成员变量
- 生命周期:随对象的生命周期而变化。
- 使用注意:即使在
const
成员函数中,mutable
变量也可以被修改。
-
thread_local(线程局部存储类):
- 范围:全局变量、函数内的局部变量或静态成员变量
- 生命周期:在每个线程的生命周期内。
- 使用注意:每个线程都有该变量的独立实例,适用于需要在不同线程中保持独立状态的数据。
2. 使用示例(Demo)
下面的代码展示了每种存储类的用法:
#include <iostream>
using namespace std;
// 全局变量,默认存储类为 extern
int g_count = 0;
void autoDemo() {
auto num = 10; // 自动类型推导
cout << "auto num: " << num << endl;
}
void staticDemo() {
static int count = 0; // 静态局部变量
cout << "static count: " << count << endl;
count++;
}
void registerDemo() {
register int regVar = 0; // 寄存器变量
cout << "register var: " << regVar << endl;
}
class Demo {
public:
mutable int mutableVar; // 可变成员变量
Demo() : mutableVar(0) {}
void setValue(int val) const {
mutableVar = val;
}
};
void threadLocalDemo() {
thread_local int threadVar = 1; // 线程局部变量
cout << "thread_local var: " << threadVar << endl;
threadVar++;
}
int main() {
// extern 变量的使用
extern int g_count; // 引用全局变量
cout << "extern g_count: " << g_count << endl;
// auto 示例
autoDemo();
// static 示例
for (int i = 0; i < 3; i++) {
staticDemo();
}
// register 示例
registerDemo();
// mutable 示例
Demo demo;
demo.setValue(100);
cout << "mutable var: " << demo.mutableVar << endl;
// thread_local 示例
threadLocalDemo();
threadLocalDemo(); // 第二次调用
return 0;
}
运行结果:
extern g_count: 0
auto num: 10
static count: 0
static count: 1
static count: 2
register var: 0
mutable var: 100
thread_local var: 1
thread_local var: 2
3. 注意事项和常见坑
-
auto
和register
:在 C++11 之后,auto
主要用于类型推导,register
的使用已逐渐被编译器优化所取代,因此一般不需要手动指定。 -
static
变量的初始化:静态局部变量在函数第一次调用时初始化,并且在后续调用中保留其值,容易导致意外行为。因此使用静态变量时要注意逻辑设计,避免错误的初始化或状态遗留。 -
extern
和 多文件编程:使用extern
时,确保在一个文件中定义全局变量,在其他文件中声明它。避免重复定义全局变量,这会导致链接错误。 -
thread_local
的性能:thread_local
变量在每个线程都有独立的副本,这会增加一定的内存开销和访问开销。需要在性能敏感的地方谨慎使用。 -
mutable
的可变性:mutable
允许在const
成员函数中修改类成员,这在设计类时要小心使用,以避免违反类的不变性原则。