咱们先看一下这个例子
int m const int n; const int *p1=&n; int *const p2=&m; const int *const p3=&n;想要弄清楚这些声明的含义最行之有效的方法就是从右向左阅读,此例中,离p2最近的符号是const,意味着p2本身是一个常量对象,对象的类型由声明符的其余部分确定。声明符中的下一个符号是*,意思是p2是一个常量指针。最后,该声明语句的基本数据类型部分确定了常量指针指向的是一个int对象。与之相似,我们也能推断出p3是一个常量指针,它指向的对象是一个int型常量。但这些一个声明中出现多个const,这const有什么区别吗?
众所周知,指针本身是一个对象,它又可以指向另外一个对象。因此,指针本身是不是常量以及指针所指的是不是一个常量就是两个相互独立的问题。用名词顶层const表示指针本身是个常量,而用名词底层const表示指针所指的对象是一个常量。
更一般的,顶层const可以扩展到任意数据类型,而底层const则与指针和引用等复合数据类型的基本类型部分(即上面例子中基本数据类型如int的左部分)有关。例如:
int i=0; int *const p1=&i; //顶层 const int ci=42; //顶层 const int *p2=&ci; //底层 const int *const p3=p2; //底层 顶层 const int &r=ci; //底层 用于声明引用的const都是底层const int &const r1=i; //非法,引用不是对象,只是指向对象的“别名”此例中也简单介绍了引用的区别
当执行对象的拷贝操作时,常量是顶层const还是底层const区别明显。其中,顶层const不受影响:
i=ci; p2=p3; p2=&i; i=*p2执行拷贝操作并不会改变被拷贝对象的值,因此,拷入和拷出的对象是否是常量都没什么影响。
另一方面,底层const的限制却不能忽视。当执行对象的拷贝操作时,拷入和拷出的对象必须具有相同的底层const资格,或者两个对象的数据类型必须能够转换。一般来说,非常量可以转换为常量,反之则不行:
int *p=p3; //非法,p3包含底层const的定义,意味着p3指向的内容不可以改变 //如果将p3赋给p,那么就可以通过p间接修改p3指向的内容,那其p3的底层const将毫无意义 p2=p3; p2=&i; //int*可以转化成const int* int &r=ci; //非法 const int &r2=i;