Appearance
static / const / volatile / extern / mutable 关键字
一、static —— 三类作用(超高频)
1. static 局部变量
cpp
int counter() {
static int n = 0; // 只初始化一次,跨函数调用保留
return ++n;
}- 存在全局/静态区(不是在栈上)。
- 首次执行到定义时初始化一次,程序结束才销毁(单线程视角)。
- 局部 static 用「局部静态初始化」保证线程安全(C++11 起编译器加锁,Meyers Singleton 基础)。
2. static 全局变量 / 函数(文件内)
- 使变量/函数具有文件作用域(internal linkage),只在本 .cpp 可见,避免其它翻译单元重名冲突。
- (现代风格倾向用
namespace {}匿名命名空间 替代static全局。)
3. static 类成员(static 数据成员 + 静态成员函数)
- static 数据成员:属于类(Class)而非某个对象,全类共享一份,需类外定义(C++17 可用
inline static成员变量)。 - static 成员函数:无 this、不能访问非 static 成员、不能是 virtual(无 this/vptr 可派发)。
- 访问:
ClassName::member或对象.也可。 - 初始化时机:程序启动于 main 之前完成(对全局 static 对象调用构造函数——「static initialization」)。
static 常问
- static 局部变量 vs 全局变量:都静态存储,区别在作用域(前者函数内的局部可见,后者全局可见)。
- 头文件里放 static 会怎样:每个 include 该头文件的翻译单元各持有一份独立 static 变量 → 不一致且耗内存。所以不要在头文件定义非 inline 全局/static。
二、const —— 各种含义(最高频过滤题)
| 写法 | 含义 |
|---|---|
const int x; | 普通常量,不可改 |
const int* p; | 指向const int:不可经 *p 改值,可改指向 |
int* const p; | 指针常量:p 不可改指向,可改 *p |
const int* const p; | 都不可 |
const T& r; | 只读引用(只读临时生命周期延长由它做,const& 能绑定 rvalue) |
T f() const;(成员) | const 成员函数:承诺不修改本对象(除 mutable),this 是 const T* |
| 顶层 const vs 底层 const | 顶层指对象本身 const,底层指指向物 const(C++ 面试进阶曾烤) |
constexpr | 编译期常量表达式(C++11),比 const 更强的常量要求,用于模板/数组大小/switch case |
常见作答要点:
- const 便于避免意外修改、可作只读接口、能用于以 4 语义确保实参不可改。
- const 成员函数可被非 const 对象调用?—— const 成员可由 const 或非 const 对象调用;非 const 成员只能被非 const 对象调用。
- 想修一个 const 对象中特定字段 →
mutable。
mutable
- 用于 const 函数内仍可修改的成员:缓存、锁、统计、
std::mutex等。 - const 成员函数中,非 mutable 成员不可改;mutable 成员可改。
三、volatile(编译期优化相关,很多人要么不懂要么乱讲)
- 告诉编译器:该变量可能被外部修改(硬件寄存器、信号处理、多线程其它 CPU 可见、中断等),每次访问都去真实内存读/写,不要优化地缓存进寄存器(禁止对它的读写进行重排合并消除)。
- 不保证原子性:volatile 不是线程同步;多线程正确性仍需 mutex/atomic。
- 常见场景:嵌入式寄存器、嵌入式标志(信号 handler 用的 sig_atomic_t 更标准)、内存映射 IO。
volatile+const可以一起用(指向 readonly 可被外部改的寄存器,如volatile const int* statusReg;)。
四、extern / extern "C"
extern声明变量/函数在其它翻译单元定义,仅声明不定义(链接用)。- 头文件一般放声明(extern),.cpp 放定义——避免多文件重复定义(ODR)。
extern "C":告诉 C++ 链接器用 C 语言名字修饰(不 mangling) 规则,从而能调用/被调用 C 写的库:
cpp
extern "C" {
#include <some_c_header.h> // 确保内部函数按 C 符号链接
void c_func(int);
}反过来 C++ 函数暴露给 C 用,也包 extern "C"。这样 ABI 一致、避免 C++ 名称修饰冲突。
五、综合记忆
- 面向「对象内/资源持有」:static 属于类;面向「访问不改」:const;面向「外部/线程可改」:volatile;跨翻译单元/跨语言:extern。
- 面试喜欢组合题:「const + static + 成员函数同时出现」「const_cast 去掉 const 后给它赋值是 UB(若原对象确实 const)」。