基本概念
一旦一个变量用const去修饰,那么这个变量的值在接下来的程序运行过程中就不能改变。如:
const int buffersize = 512;//const 整形常量初始化
buffersize = 256;//错误,因为buffersize已经定义为整型常量无法修改
以下是相关const用法一些小细节。
const常量初始化
int i = 42;
const int ci = i;//正确
int j = ci;//正确
如果利用一个对象去初始化另外一个对象,则它们是不是const都无关紧要。尽管ci是整形常量,但常量的特征只在执行改变ci的操作中起作用。当用ci去初始化j时,根本无需在意ci是不是一个常量。
另外默认状态下,const对象仅在本文件内有效
const int bufsize = 512;
编译器在编译过程中把用到该变量的地方替换成对应的值。如上面所示,编译器会在代码中用到bufsize的地方,然后用512替换。
如果程序包含多个文件,为了能够使编译器完成代码的替换,则必须在每个文件里定义同名的const变量,其实等同于在不同的文件里定义不同的独立变量。
为了使在不同文件使用相同的const变量,可以用下列解决方法:
//file1.cpp定义并初始化一个常量
extern const int bufsize = fcn();
//file1.h头文件
extern const int bufsize;
因此如果想在多个文件之间共享const对象,必须在变量的定义之前添加extern关键字。
const 常量引用
常量引用与普通的引用不同,对常量的引用不能被用作修改它所绑定的对象。
const int ci = 1024;
const int &ri = ci;
ri = 42;//错误,ri是对常量的引用
int &r2 = ci;//错误,试图让一个非常量引用指向一个常量对象
也就是说非常量引用不能指向常量对象
初始化和对const的引用
引用的类型必须与其所引用的对象的类型一致。但对于常量引用有两个例外
(1)在初始化常量引用时允许用任意表达式作为初始值,只要该表达式的结果被转换成引用的类型即可。
int i = 42;
const int &r1 = i;
const int &r2 = 42;
const int &r3 = r1*2;
int &r4 = r1 * 2;//错误
具体原因是常量引用时存在一个临时变量,而常量引用绑定的是这个 临时变量,如果非常量引用绑定这个临时变量,那么对引用的改变就无法作用在引用的对象上。
指针常量与常量引用类似
归根到底一句话:指向常量的指针或引用,不过是指针或引用“自以为是”罢了,他们觉得自己指向了常量,所以自觉地不去改变所指对象的值。
顶层const
指针本身是一个对象,它又可以指向另外一个对象。因此,指针本身是不是常量以及指针所指的是不是一个常量就是两个相互独立的问题。用名词顶层const表示指针本身是个常量,而用名词底层const表示指针所指的对象是个常量。
更一般的,顶层const可以表示任意的对象是常量,这一点对任何数据类型都适用。而底层const与指针和引用等复合类型的基本类型部分有关。
当执行拷贝操作并不会改变被拷贝对象的值,因此拷入和拷出的对象是否是常量都没什么影响。即顶层const影响不大。
另一方面,底层const的限制不能忽视。当执行拷贝操作时,拷入和拷出的对象必须具有相同的底层const资格,或者二者的数据类型能够转换。一般来说,非常量可以转换成常量。
constexpr和常量表达式
常量表达式是指值不会改变并且在编译过程中就能得到计算结果的表达式。显然,字面值属于常量表达式,用常量表达式初始化的const对象也是常量表达式。
声明为constexpr的变量一定是个常量,而且必须用常量表达式初始化。
constexpr 指针不能指向函数体内的变量,相反函数体外的变量固定地址不变,可以用constexpr修饰。
注:在constexpr声明中如果定义了一个指针,限定符constexpr仅对指针有效,与指针所指的对象无关。
const int *p = nullptr;//指向常量的指针
constexpr int *q = nullptr;//指向int变量的常量指针
constexpr const int *r = &i;//指向常量的常量指针