const限定符
2.4 初始化和const
- const对象一旦创建后其值就不能再改变,所以const对象必须初始化,并且可以是任意复杂表达式。
- 如果利用一个对象去初始化另一个对象,则它们是不是const都无关紧要,例如:
int i = 42; const int ci = i; //正确:i的值被赋给了ci int j = ci; //正确:ci的值被赋给了j
为什么呢,因为ci的常量特征仅在执行改变ci的操作时才会发挥作用(不允许修改ci的值),注重的是过程,而与到底是谁,是哪种类型初始化ci没有关系。
- 默认状态下,const对象仅在文件内有效
解决办法是,对于const对象,不管是在文件中声明还是定义都要添加extern关键字。
//file.c 定义并初始化了一个常量,并且该常量能被其他文件访问 extern const int bufsize = fcn(); //file.h 头文件 extern const int bufsize;
2.4.1 const的引用:对常量的引用
与普通引用(引用类型必须与引用对象类型一致)不同,常量引用:
- 在初始化常量引用时 ,允许用任意表达式作为初始值。
- 允许为常量引用确定非非非非非非常量的对象,字面值,甚至是一般表达式。
两种经典错误:
(1)
const int ci = 1024; const int &r1 = ci; r1 = 42; //错误,常量引用r1不允许修改常量 int &r2 = ci; //错误:ci是常量不允许修改,但是r2是普通引用,存在修改ci的风险
不能用非常量的引用指向一个常量!!!
(2)
double dval = 3.14; const int &ri = dval;
ri引用了一个int类型的数,其对象也应该是int类型的,但是dval却是一个浮点数。因此为了确保让ri能够绑定一个整数,编译器把上述代码变成了如下:
const int temp = dval; //让浮点数生成一个临时的整型常量 const int &ri = temp; //让ri绑定这个临时的常量
乍一看其实没啥毛病,但是仔细想想,我的初心就是想让ri绑定dval,让ri能够修改dval的值,但是从底层来看,ri其实绑定了temp,也就是说无论我怎么修改ri,dval的值都不会被修改。那这样就没有意义了,所以这个代码运行是没问题的,但其实是非法的。
对const的引用可能引用一个并非const的对象
必须认识到,常量引用仅仅对引用可参与的操作做出了限定,对于引用的对象本身是不是常量并未做限定,例如:
int i = 42; int &r1 = i; //引用r1绑定i const int &r2 = i; //常量引用r2也绑定i,但是不允许修改i的值 r1 = 0; //允许 r2 = 0; //不允许
代码中,不允许通过修改r2修改i的值,尽管如此,i的值仍然可以通过其他途径修改(r1,i)。
2.4.2 指针和const
这部分内容,请看上一篇博客(引用和指针的区别)。
2.4.3 顶层const
如前所述,指针本身是一个对象,但是它又可以指向另外一个对象。因此存在两个独立的问题:指针本身是不是常量;指针所指的对象是不是常量。
- 顶层(top-level const):指针本身是一个常量
- 底层(low-level const):指针所值的对象是一个常量
2.4.4 constexpr和常量表达式
常量表达式(const expression)是指: 值不会改变并且在编译过程中就能得到计算结果的表达式。
get_size()函数 -- 直到运行时才能获取到,所以也不是常量表达式