(一)const限定符
有时我们希望定义一种这样的变量,它的值不能被改变。例如,用一个变量来表示缓冲区的大小。使用变量的好处是当我们觉得缓冲区大小不再合适时,很容易对其进行调整。另一方面,也应随时警惕防止程序一不小心改变了这个值。为了满足这一个要求,可以用关键字const对变量的类型加以限定:
const int bufSize = 512;//输入缓冲区大小
这样就把bufSize定义为一个常量,因为是常量,所以一旦创建后其值就不能改变,所以const对象必须初始化。
某些时候有这样一种const变量,它的初始值不是一个常量表达式,但又确实有必要在文件间共享。这种情况下,我们不希望编译器为每个文件分别生成独立的变量。相反,我们想让这类const对象像其它(非常量)对象一样工作,也就是说,只在一个文件中定义const,而在其它多个文件中声明并使用它。
解决办法就是对于const变量不管是声明还是定义都添加extern关键字,这样只需要定义一次就可以了。
(二)const的引用
可以把引用绑定到const对象上,就像绑定到其他对象上一样,我们称之为对常量的引用。与普通引用不同的是,对常量的引用不能被用作修改它所绑定的对象:
const int ci = 1024;
const int &r1 = ci;//正确,引用及其对应的对象都是常量
r1 = 42;//错误,r1是对常量的引用
int &r2 = ci;//错误,试图让一个非常量引用指向一个常量对象
对const的引用可能引用一个并非const的对象。必须认识到,常量的引用可参与的操作做出了限定,对于引用的对象本身不是一个常量未作限定。因为对象也可能是一个非常量,所以允许通过其他途径改变它的值。
int i = 42;
int &r1 = i;//引用ri绑定对象i
const int &r2 = i;//r2也绑定对象i,但是不允许通过r2修改i的值
r1 = 0;//正确,r1并非常量
r2 = 0;//错误,r2是常量
(三)const指针
指针是对象而引用不是,因此就像其它对象类型一样,允许把指针本身定为常量。常量指针必须初始化,而且一旦初始化完成,则他的值(也就是存放在指针中的那个地址)就不能再改变了。把*放在const前面用以说明指针是一个常量,这样的书写形式隐含着一层意味,即不变的是指针本身的值而非指向的那个值:
int errNumb = 0;
int *const curErr = &errNumb;//curErr将一直指向errNumb
const double pi = 3.1419;
const double *const pip = π//pip是一个指向常量对象的常量指针
(四)顶层const
如前所述,指针本身是一个对象,它又可以指向另外一个对象。因此,指针本身是不是常量以及指针所指的是不是常量就是两个相互独立的问题。用名词顶层const表示指针本身是个常量,而用名词底层const表示指针所指的对象是一个常量。
int i = 0;
int *const p1 = &i;//不能改变p1的值,这是一个顶层const
const int ci = 42;//不能改变ci的值,这是一个顶层const
const int *p2 = &ci;//允许改变p2的值,这是一个底层const
const int *const p3 = p2;//靠右的const是顶层const,靠左的是底层const
const int &r = ci;//用于声明引用的const都是底层const
(五)constexpr和常量表达式
常量表达式是指不会改变并且在编译过程就能得到计算结果的表达式。显然,字面值属于常量表达式。用常量表达式初始化的const对象也是常量表达式。
一个对象(或表达式)是不是常量表达式由它的数据类型和初始值共同决定的,例如:
const int max_files = 20;//是常量表达式
const int limit = max_files + 1;//是常量表达式
int staff_size = 27;//不是常量表达式
const int sz = get_size();//不是常量表达式
尽管staff_size的初始化是一个字面值常量,但是由于他的数据类型只是一个普通的int而非const int,所以他不属于常量表达式。另一方面,尽管sz本身是一个常量,但是它的具体值直到运行时才能获取,所以也不是常量表达式。
在一个复杂系统中,很难(几乎肯定不能)分辨一个初始值到底是不是常量表达式。
c++11新标准规定,允许将变量声明为constexpr类型以便由编译器来验证变量的值是否是一个常量表达式。声明为
constexpr的变量一定是一个常量,而且必须用常量表达式初始化:
constexpr int mf = 20;//20是常量表达式
constexpr int limit = mf + 1;//mf+1是常量表达式
constexpr int sz = size();//只有当size是一个constexpr函数时才是一条正确的声明语句