本节书摘来自异步社区出版社《C++覆辙录》一书中的第1章,第1.6节,作者: 【美】Stephen C. Dewhurst(史蒂芬 C. 杜赫斯特),更多章节内容可以访问云栖社区“异步社区”公众号查看。
.6:对常量(性)的认识误区
在C++中的常量性概念是平凡的,但是这和我们对const
先入为主的理解不太符合。首先我们要特别注意以const
饰词修饰的变量声明和字面常量的区别:
int i = 12;
const int ci = 12;```
字面常量12不是C++概念中的常量。它是个字面常量。字面常量没有地址,永远不可能改变其值。i是个对象,有自己的地址,其值可变。用const关键字修饰声明的ci也是个对象,有自己的地址,尽管在本例中其值不可变。
我们说i和`ci`可以作为左值使用,而字面常量12却只能作为右值。这两个术语来源于伪表达式`L=R`,说明只有左值能出现在赋值表达式左侧,右值则只能出现在赋值表达式右侧。但这种定义对C++和标准C来说并不成立,因为在本例中`ci`是左值,但不能被赋值,因为它是个不可修改的左值。
如果把左值理解为“能放置值的地方”,那么右值就是没有与之相关的地址的值。
int *ip1 = &12; // 错误!
12 = 13; // 错误!
const int *ip2 = &ci; // 没问题
ci = 13; // 错误! `
最好仔细考虑一下上面ip2
的声明中出现的const
,这个饰词描述了对我们通过ip2
对ci
的操作的约束,而不是对于ci的一般操作的约束21
。如果我们想声明指涉到常量的指针,我们应该这么办:
const int *ip3 = &i;
i = 10; // 没问题,约束的不是i的一般操作而是通过ip3对i的操作
*ip3 = 10; // 错误! ```
这里我们就有了一个指涉到`const int`型别的指针,而这个`const int`型别对象又是一个普通int型别对象的引用。这里的常量性仅仅限制了我们能通过ip3做什么事。这不表明i不会变,只是对它的修改不能通过ip3进行。如果我们再把问题说细一点,请看下面这个把`const`和`volatile`结合使用的例子:
extern const volatile time_t clock;`
这个const饰词的存在表明我们未被允许(在代码中显式地直接)修改变量clock
的值,但是同时存在volatile
饰词说明clock的值肯定还是会通过其他途径发生变更22。