一、引言
有时我们希望定义一种变量,它的值不会被改变,可以根据实际需要通过某种方式调整,也可以警惕防止程序一不小心改变这个值,我们以前常用#define来实现这个功能。但是,有时出于某些因素,我们使用 const 对变量类型加以限定。
const int bufsize=512; //缓冲区大小
二、const初始化问题
首先,分析一下const的初始化问题。const类型的对象可以完成大部分非const类型的操作,但在const类型的对象上只能执行不改变其内容的操作。在不改变const对象的操作中就有一种是初始化,也就是说,如果利用一个对象去初始化另一个对象,那么无论是不是const类型都无关紧要。分析几个示例可以发现:
int i=5;
const int ci=i; //正确:使用i来初始化
const int cj=6; //正确:使用常量来初始化
int j=ci; //正确:const类型可以参与不改变值的操作
int k=ci++; //错误:试图通过修改const类型的值
const int ck; //错误:没有初始化
三、编译器如何处理const类型
接下来,我们谈论一下const类型的工作原理,这样就可以了解为什么不能修改const类型的值,或是为何定义时必须用一个值来初始化它。
编译器在编译过程中把用到该变量的地方都替换成对应的值。也就是说,对于引言中bufsize的例子,编译器会找到所有bufsize的地方,然后用512替换。这个似乎和#define类似,但是#define是在预处理过程中替换,而const是在编译过程中替换,如果报错,则会指出错误的变量,方便debug。通过下面的例子可以观察到不同:
#include<iostream>
int main()
{
const int i=5;
i++;
return 0;
}
报错信息:
error: increment of read-only variable 'i'|
#include<iostream>
#define MAX 5
int main()
{
MAX++;
return 0;
}
报错信息:
error: lvalue required as increment operand|
介绍完基础的内容就可以考虑更深层次的用法: 比如const的引用、指针和const等问题
四、const的引用
可以把引用绑定到const对象上,称为 对常量的引用(regerence to const)。
我们前面讨论的情况下,用作const初始化的对象在const初始化后就与const脱离了关系,就相当于两个不同的变量。比如ci和i,初始化结束后,两个变量之间便没有任何联系,i仍可以修改自己的值,ci不能修改自己的值。
但是根据我们学习的关于引用的知识(简单理解为一个对象的另一个名字,它们的关系是绑定而不是拷贝),与普通引用不同的是,对常量的引用不能被用作修改它所绑定的对象。
int i=6;
const int &ci=i; //正确:引用及其对应的对象都是常量
i=7; //正确:i=7 ci=7 利用i修改了ci的值
ci=8; //错误:ci是对常量的引用
const int &cj=3; //正确
int &j=3; //错误:试图让一个非常量引用指向一个常量对象
注意,与普通引用不同的还有一点,普通引用两个对象类型必须相同。而const引用则不必须,允许为一个常量引用绑定非常量的对象、字面值,甚至一个表达式。
double i=6.6;
const int &ci=i; //正确:绑定double类型的值,ci=6
int &j=i; //错误:必须类型相同
总结一点:只要不通过常量引用来修改对象的值,别的都是合法的。const引用可以用任意可转化类型的变量初始化。
五、指针和const
指针本身也可以定义为常量,称为 常量指针(const pointer),并且符合const特性。在这里我们讨论两种类型,根据结构就可以分辨出来它们的不同。
(1)指针是一个常量
这种情况表示一旦初始化完成,指针的值就不会改变,也就是存放在指针中的地址不会改变。
int i=6;
int j=7;
int *const ci=&i; //正确:初始化常量指针
ci=&j; //错误:试图改变常量指针的值
i=7; //正确
*ci=8; //正确:常量指针具体值可以改变
但是需要注意,正如上面说的,指针是一个常量,也就是我们可以通过ci或者i直接修改具体值,但只要不修改ci的指针值(注意下图中的情况)。
int k;
int *i=&k;
int *const ci=i;
ci=&k; //错误:虽然它们地址都一样,但还是不能修改ci的指针值
(2)指针所指的对象是一个常量
与正常的const类型一样,这种情况下具体对象的值也不能改变。
int i=6;
const int *const ci=&i;
i=7;
*ci=8; //错误:底层const 不能修改具体值
六、顶层const和底层const
顶层const(top-level const) 指针本身是个常量 更一般的,它也可以表示任意对象是常量
底层const(low_level const) 指针所指对象是一个常量,和指针、引用相关
int i=0;
int *const p1=&i; //顶层const,不能改变p1的值
const int ci=42; //顶层const,不能改变ci的值
const int *p2=&ci; //底层const,允许改变p2的值
const int *const p3=p2; //左边是底层const 右边是顶层const
const int &r=ci; //底层const
七、const成员函数
在学习类时,有一种称为const成员函数的函数,下面给出一个示例: 注意到const的位置在函数参数列表的后面
int getvalue() const
{
return this->num;
}
const成员函数,不允许函数中修改类成员变量(mutable 类型除外),而一些作为参数的外部变量则可以修改。