首先:顶层const表示指针本身是个常量,底层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
如何区分顶层const与底层const
低层 const:
个人的理解:声明指向常量的指针就是底层const。
int num1 = 1;
int const *p2 = &num1; //底层const
//*p2 = 2; //error C3892: “p2”: 不能给常量赋值
num1 = 2;
std::cout<<*p2<<"\n"<<std::endl; //输出为2
注意:“常量指针”不一定非要指向常量,只是不能通过解引用符(*)来改变它所指向的内容。
顶层 const
顶层const可以表示任意的对象是常量,这一点对任何数据类型都适用。对于指针:要求本身是常量,声明时必须初始化,之后它存储的地址值就不能再改变,可以通过解引用符(*)来改变它所指向的内容。
int num1 = 1, num2 = 2;
int *const p1 = &num1; //顶层const
//p1 = &num2; //error C3892: “p_b”: 不能给常量赋值
*p1 = 2;
std::cout<<*p1<<"\n"<<std::endl; //输出为2
对于指针,一个指针本身添加const限定符就是顶层const,而指针所指的对象添加const限定符就是底层const。
const int *p2 = &ci; //p2是const int * 类型(指向整型常量的指针),是底层const
int *const p1 = &i; //p1是int * 类型常量(指针常量),是顶层const
const int *const p3 = p2; //靠右的const是顶层const,靠左的是底层const
//p3是const int * 型常量。
顶层const与底层const的作用
对象拷贝时顶层const不受什么影响,拷贝操作不会改变被拷贝对象的值。
对底层const却有限制,常量的底层const不能赋值给非常量的底层const。
int i = 0;
const int ci = 42; //顶层const
const int *p2 = &ci; //底层const
const int *const p3 = p2; //既是顶层const,又是底层const
p2 = p3; //都是底层const(p3既是底层const又是顶层const)
p2 = &i; //int *, 可以转换为 const int *
//int &r = ci; //int& 不能绑定到const int类型 (error C2440: “初始化”: 无法从“const int”转换为“int &” 转换丢失限定符)
const int &r2 = i; //const int& 能绑定到int类型