有时我们希望定义这样一种变量,它的值不能被改变。例如,用一个变量来表示缓冲区的大小。使用变量的好处是当我们觉得缓冲区大小不再合适时,很容易对其进行调整。另一方面,也应随时警惕防止程序不小心改变了这个值。为了满足这一要求,可以用关键字 const 对变量的类型加以限定:
const int bufSize = 512; // 输入缓冲区大小
使用 const 要注意以下几点:
1. const 对象被定义成了一个常量,任何试图改变其值的行为都会引发错误。
2. const 对象使用前必须初始化,即需要将 const 对象定义为一个常量。
3. 不能由一个非常量引用一个常量。
const int ci = 1024;
int &r = ci; // 错误,试图让一个非常量引用指向一个常量对象。
4. 何为使用 const 非法? 举例如下:
double dval = 3.14;
const int &ri = dval;
以上操作是想将一个常量引用ri绑定到另一种类型上。此处ri引用了一个int型的数。对ri的操作应该是整数运算,但dval却是一个双精度浮点数而非整数。因此为了确保让ri绑定一个整数,编译器把上述代码变成了如下形式:
const int temp = dval; // 由双精度浮点数生成一个临时的整型常量
const int &ri = temp; // 让ri绑定这个临时量
这样,引用就绑定到临时量了,C++语言就把这种行为归为非法。
5. const 指针的意义
const double pi = 3.14;
const double *cptr = π // 此为指向常量的指针,不能改变pi的值,但可以改变指针p的指向。
const double *const q = π // q是一个指向常量对象的常量指针
double m = 1.0;
cptr = &m; // 正确。
q = &m; // 错误。
6.顶层 const
用名词顶层const表示指针本身是个常量,而用名词底层const表示指针所指的对象是一个常量。
int i = 0;
int *const pl = &i; // 不能改变pl的值,这是一个顶层const
const int ci = 42; // 不能改变ci的值,这是一个顶层const
const int *p2 = &ci; // 允许改变p2的值,这是一个底层const(指针所指对象为常量)
const int *const p3 = p2; // 靠右的const是顶层const,靠左的const是底层const(原理如上)
const int &r = ci; // 用于声明的const都是底层const
7. constexpr和常量表达式
常量表达式是指值不会改变并且在编译过程就能得到结果的表达式。
const int max_files = 20; // max_files是常量表达式
const int limit = max_files + 1; // limit 是常量表达式
int staff_size = 27; // staff_size不是常量表达式
const int sz = get_size(); // sz不是常量表达式
c++11新标准规定,允许将变量声明为constexpr类型以便由编译器来验证变量的值 是否是一个常量表达式。声明为constexpr的变量一定是一个常量,而且必须用常量表达式初始化:
constexpr int mf = 20; // 20是常量表达式
const int limit = mf + 1; // mf + 1是常量表达式
constexpr int sz = size(); // 只有当size是一个constexpr函数时才是一条正确的声明语句
重点:在constexpr声明中如果定义了一个指针,限定符constexpr仅对指针有效,与指针所指的对象无关:
const int *p = nullptr; // p是一个指向整型常量的指针
constexpr int *q = nullptr; // q是一个指向整数的常量指针
其中关键在于constexpr把它所定义的对象置为了顶层const。