指针本身是一个对象,它可以指向另一个对象。因此,指针本身是不是常量和指针所指的对象是不是常量就是两个独立的问题。
顶层const表示指针本身是个常量。
底层const表示指针所指的对象是个常量。
下面所示,pi就是一个常量指针,不能修改pi的值。而pd则是一个指向常量的常量指针,既不能修改pd的值,也不能通过pd修改其指向对象的值。
int i = 0;
int *const pi = &i;
const double d = 3.14;
const double *const pd = &d;
顶层const可以用于任意对象类型。底层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为底层const
const int &r = ci; // 用于声明应用的const都为底层const
当执行对象拷贝时,常量是顶层const还是底层const区别明显。其中,顶层const对拷贝没什么影响:
i = ci;
p2 = p3;
底层const则要求拷入和拷出对象必须有相同的底层const资格,或者两个对象的数据类型必须能够转换。非常量可以转换为常量,反之则不行:
int *p = p3 // 错误
p2 = p3; // 正确
p2 = &i; // 正确
int &r = ci; // 错误
const int &r2 = i; // 正确