const 限定符
如果我们希望定义一个变量,但是它的值是不可改变的。这种需求在很多方面都是有的。比如,用一个变量来表示缓冲区的大小,这时候我们想定义一个这样的值,在程序中定义好之后不能再别的地方再改变它的值。那么我们可以使用关键字 const 对变量的类型加以限定:
const int buffSize = 1024;
这样就把 buffSize 定义成为了一个常量,任何试图在程序的别的地方赋值 buffSize 的行为都会引发错误。因为 const 对象一旦创建后其值就不能再改变,所以 const 对象必须初始化。
cosnt int i = get_size(); // 正确
const int j; // 错误,未初始化
我们知道对象类型决定了对象的操作。与非 const 类型所能参与的操作相比, const 类型的对象能完成正常类型所有的大部分操作,但也不是所有的操作都适合。主要的限制就是只能在 const 类型的对象上执行不改变其内容的操作。例如,const int 和 int 一样能参与算术运算,等等。
在不改变 const 对象的操作中还有一种操作是初始化,如果利用一个对象去初始化另外一个对象,则它们是不是 const 都无关紧要:
int i = 1024;
const int j = i; // 正确:i 的值被拷贝给了 j
int k = j; // 正确:j 的值被拷贝给了 k
可以把引用绑定到 const 对象上,就像绑定到其他对象上一样,我们称之为对常量的引用。 与普通引用不同的是,对常量的引用不能被用作修改它所绑定的对象。
const int ci = 1024;
const int &ri = ci; // 正确:引用及其对应的对象都是常量
ri = 42; // 错误:ri 是对常量的引用
int &r2 = ci; // 错误:试图让一个非常量引用指向一个常量对象
因为不允许直接为 ci 赋值,当然也就不能通过引用去改变 ci。因此,对 r2 的初始化是错误的。
假设该引用初始化合法,则可以通过 r2 来改变它引用对象的值,这显然不正确。
引用类型必须与其所引用对象的类型一致,但是有两个例外。
(1)初始化常量引用时允许用任意表达式作为初始值,只要该表达式的结果能转换成引用的类型即可。尤其,允许为一个常量引用绑定非常量的对象、字面值,甚至是个一般的表达式:
int i = 1024;
const int &r1 = i; // 正确:允许将 const int& 绑定到一个普通 int 对象上
const int &r2 = 1024; // 正确: 常量引用
const int &r3 = r2 * 2; // 正确:允许表达式
int &r4 = r1 * 2; // 错误:r4 是一个普通非常量引用
要想理解这种例外情况发生的原因,最简单的办法就是弄清楚当一个常量引用被绑定到另外一种类型上时到底发生了什么:不能改变其所指
double dval = 3.1415;
const int &r1 = dval;
为了上述行为,编译器把上述代码变为了如下形式:
const int tmp = dval;
const int &r1 = tmp; // 生成了一个临时变量,然后引用它
(2)常量引用仅对可参与的操作做出了限定,对于引用的对象本身是不是一个常量未做限定。因为对象也可能是个非常量,所以允许通过其他途径改变它的值。
与引用一样,也可以令指针指向常量或非常量。类似于常量引用,指向常量的指针不能改变其所指对象的值。要想存放常量对象的地址,只能使用指向常量的指针:
const double pi = 3.14;
double *ptr = π // 错误:ptr 是一个普通的指针
const double *cptr = π // 正确:cptr 可以指向一个常量
*cptr = 42; // 错误:不能再赋值
指针类型必须与其所指对象的类型一致,但是有两个例外,也就是以上的两种。
- const 指针
指针是对象而引用不是,因此就像其他对象类型一样,允许把指针本身定义为常量。常量指针必须初始化,而且一旦初始化完成,则它的值(也就是存放指针那个地址)就不能改变。把 * 放在 const 关键字之前用来说明指针是一个常量,这样意义就是不变的是指针本身的值而不是指向的那个值。
指针本身是个对象,它又可以指向另外一个对象。因此,指针本身是不是常量以及指针所指的是不是一个常量就是两个相互独立的问题。用名词顶层 const, 表示指针本身是个常量,而用名词底层 const, 表示指针所指的对象是一个常量。
- 顶层 const
int i = 0;
int *const pi = &i; // 不能改变 pi 的值,这是顶层 const
const int ci = 1024; // 不能改变 ci 的值,这是顶层 const
- 底层 const
int i = 0;
const int *p2 = &ci; // 允许改变 p2 的值,这是底层 const
const int *const p3 = &p2; // 靠右的 const 是顶层 const, 靠左的是底层 const
const int &r = ri; // 用于声明引用的 const 都是底层 const