上节课我讲了自动类型推导,提到 auto 推导出的类型可以附加 const、volatile 修饰(通常合称为“cv 修饰符”)。别看就这么两个关键字,里面的“门道”其实挺多的,用好了可以让你的代码更安全、运行得更快。今天我就来说说它们俩,以及比较少见的另一个关键字 mutable。
const 与 volatile
先来看 const 吧,你一定对它很熟悉了。正如它的字面含义,表示“常量”。最简单的用法就是,定义程序用到的数字、字符串常量,代替宏定义。
const int MAX_LEN = 1024;
const std::string NAME = "metroid";
但如果我们从 C++ 程序的生命周期角度来看的话,就会发现,它和宏定义还是有本质区别的:const 定义的常量在预处理阶段并不存在,而是直到运行阶段才会出现。
所以,准确地说,它实际上是运行时的“变量”,只不过不允许修改,是“只读”的(read only),叫“只读变量”更合适。
既然它是“变量”,那么,使用指针获取地址,再“强制”写入也是可以的。但这种做法破坏了“常量性”,绝对不提倡。这里,我只是给你做一个示范性质的实验,还要用到另外一个关键字 volatile。
// 需要加上volatile修饰,运行时才能看到效果
const volatile int MAX_LEN = 1024;
auto ptr = (int*)(&MAX_LEN);
*ptr = 2048;
cout << MAX_LEN << endl; // 输出2048
可以看到,这段代码最开始定义的常数是 1024,但是输出的却是 2048。
你可能注意到了,const 后面多出了一个 volatile 的修饰,它是这段代码的关键。如果没有这个 volatile,那么,即使用指针得到了常量的地址,并且尝试进行了各种修改,但输出的仍然会是常数 1024。
这是为什么呢?
因为“真正的常数”对于计算机来说有特殊意义,它是绝对不变的,所以编译器就要想各种办法去优化。
const 常量虽然不是“真正的常数”,但在大多数情况下,它都可以被认为是常数,在运行期间不会改变。编译器看到 const 定义,就会采取一些优化手段,比