const限定符
c++中默认const对象仅在当前文件内有效。这是因为编译器在编译过程中把用到该const变量的地方全都替换成对应的值,当然为了完成上述替换动作,const对象就必须被初始化。
为了能够在别的源文件中也能使用某个const变量,可以在声明和定义的地方都加上extern修饰。可以理解为用extern来抵消const所带来的链接属性的影响。
const通常需要注意的两点:
- const引用
- 顶层const以及底层const
const引用
对于const引用,有一点要注意的是在初始化常量引用的时候,允许用任意表达式作为初始值,前提是只要该表达式的结果能够转化成引用的类型即可。如下都是可以的:
int i = 42;
const int &r1 = i;
const int &r2 = 42;
int &r3 = 42;//error 非常量的引用的初值必须为左值
其实这是很容易理解的,因为const引用,即相当于我们告诉编译器,不能通过该引用去修改所引用对象的值。即考虑如下代码:
double i = 42.3;
const int &r1 = i;
为了确保上述代码中r1绑定到一个int类型的变量上,编译器实际上会把上述代码变成:
double i = 42.3;
const int temp = i;
const int &r1 = temp;
即r1实际上是绑定到了这个临时对象上。之所以能够这么做,是因为我们不会通过常量引用去修改该对象。
顶层const与底层const
顶层const即const修饰的对象本身是const,这对于任何数据类型都适用。但是底层const则是与指针或者引用等复合类型的基本类型部分有关。
如下:
int i = 0;
int *const p1 = &i;// top-level const, 顶层const,不能修改p1的值
const int * p2 = &i;// low-level const,底层const,不能通过p2修改i的值
const int &r1 = i;//用于声明引用的const都是底层const
两种所带来的区别是对于在执行拷贝操作的时候,顶层const的影响可以忽略,但是拷入和拷出的对象必须具有相同的底层const资格,或者两个对象的数据类型必须能够转换,一般非const能够转换为const,如下:
int i = 0;
const int * const p3 = &i;//ok
const int j = 0;
int *const p4 = &j;//error 不能用const int *的对象初始化int*的对象
const int * p5 = &i;//ok, 非const能够转换为const