const
限定符
当我们需要一个变量它的值不能被改变时,我们可以用关键词const
对变量的类型加以限定。这时此const
类型只能执行不改变其内容的操作。
初始化
由于const
对象一旦被创建后就能不能被改变,所以const
对象必须初始化。
const int i=sizeof(int); // 正确
const int j=1; // 正确
const int m; // 错误,必须初始化
编译时,编译器将会在用到该变量的地方都替换成对应的值。默认情况下,const
对象被设定为仅在文件内有效。当需要在多个文件中使用这个const
对象时,需要加上extern
限定符
const
引用
当把一个引用绑定到const
对象上时,不能修改它所绑定的对象,称为对常量的引用。同时,const
类型的对象只能被const
引用绑定。
const int a=9;
const int &b=a; // 正确
b=1; // 错误,const引用不能修改绑定的对象的值
int &c=a; // 错误,const对象只能被const引用绑定
引用类型中的例外
引用的类型必须与其所引用对象的类型一致,但在初始化常量引用时,允许用任意表达式作为初始值:
double val=1.5;
const int &re=val; // 正确
此时该表达式的值会转换成引用的类型,在编译过程中,编译器会产生一个临时的对象让re绑定:
double val=1.5;
const int temp=val;
const int &re=temp;
允许这样做的原因是:当re不是常量时,表示人们想要对val进行修改。然而这个修改只能改变temp,而无法改变val。这个行为时无效的,因此被归为非法行为。
const
引用对非const
对象的引用
const
引用只对修改做出了限定,并未对被引用的对象本身做出限定。因此被引用的对象也可以为一个变量。同时,该变量的值修改了,const
引用的值也会被修改。
int a=0;
const int &b=a; // 正确,初始化允许常量引用时允许用任意表达式作为初始值
a=1; // 正确,此时b的值为1
const
指针
指针是对象而引用不是,因此可以把指针本身定为常量。同样的,常量指针必须初始化。需要清楚的一点是:常量指针不能改变的值是指针的值而不是指针指向的地址的值。通常,我们把*放在const
关键字以前说明该指针是一个const
指针。这样隐含着一个信息:不变的是指针而不是指针指向的值。
int a=9;
int *const p1=&a; // 正确
int b=1;
p1=&b; // 错误,p1将会永远指向a
*p1=1; // 正确,p1指向的值a是可以改变的
const int c=10;
const int *const p2=&c; // 正确,表示一个指向常量的常量指针
*p2=1; // 错误,p2是一个指向常量的指针
int *p3=&c; // 错误,只有const指针才能指向const类型的变量
不同位置的const
含义判断
判断的依据是从右向左阅读,如下:
const int a=1;
const int b=0;
const int *p1=&a; // 正确,表示p1指向的是一个const int类型的值
const int *const p2=&b; // 正确,表示p1是一个const指针,指向的是一个const int类型的值
p1=&b; // 正确,因为p1不是const指针,它只是指向一个const int类型的值
p2=&a; // 错误,因为p2是一个const指针,所以它的值不能被改变
顶层const
和底层const
在这里,我们使用顶层const
表示指针本身是一个常量;使用底层const
表示指针所指的对象是一个常量。
推广以后,顶层const
表示任意的对象是常量;底层const
则表示指针和引用等复合类型的基本类型部分有关。
const
和宏的区别
- 编译器处理:
define
在预处理的时候展开;const
在编译中替换 - 类型和安全检查:
define
没有类型,仅作展开;const
有具体类型,编译时会做类型检查 - 存储方式:
define
仅作展开,不分配内存;const
会在内存中分配内存
注意点
const
对象必须初始化const
对象仅在该文件中有效const
成员变量不能在声明是初始化