const指针
指针和引用不一样,指针本身是对象,所以可以定义为常量,而引用不行,有常量指针而没有常量引用。常量指针必须初始化,而且一旦初始化,其值(也就是指针存的地址)就不能在改变。把*放在const关键字之前用以说明指针是个常量,这隐含不变的是指针本身(即指针存的地址)的值不变而非指向的那个值;
还有一种就是指向常量的指针,即*在const后面,表示指针本身并非常量,即指针的值(指针存的地址)可以变,而指针指向的值不可以变,也有的叫指针常量(并不准确)。
int i = 0,x = 1;;
int *const j = &i;//j是常量指针,j的值是i的地址,不可以变,但是可以改变j指向的值;
//j = &x;错误,不能改变j的地址
//*j = 5;正确,可以改变j指向的值,此时i = 5;
const int *k = &i;//k是一个指向常量的指针,注意到这里并不需要指向的一定是是常量,因为编译器会自作聪明的认为指向的是常量
//因为指针并不是常量,所以可以修改指针的地址
//k = &x;正确,k指向x,只要指向的仍是cont后修饰的类型,但是不能通过*k = 5这样来改变x(k所指的值)的值
const double pi = 3.14;
const double *const pip = π//pip是一个指向常量的常量指针,所以不能修改pip的值和pip所指的值
顶层const和底层const
如上文所讲,指针本身是不是常量和指针所指的是不是一个常量就是两个相互的问题,所以有顶层const和底层const的出现;
顶层const:表示指针本事是个常量
底层const:表示指针所指的对象是一个常量。
更一般的,顶层const可以表示任意的对象是常量,这对任何数据类型都适用,如int等算术类型、类、指针等。底层const则与指针和引用等复合类型的基本类型部分有关。比较特殊的是,指针类型既可以是顶层const也可以是底层const,这一点和其他类型相比区别明显;
int i = 0;
int *const p1 = &i;//这是个指针常量,也就是顶层const,p1的值(p1存的地址)不能改变
const int ci = 42;//ci的值不能改变,是顶层const,const int == int const;
const int *p2 = &ci;//p2指向的是常量,但p2不是,这是个底层const
const int *const p3 = p2;//靠右的const是顶层const,靠左的是底层const,即又是顶层const也是底层const;
const int &r = ci;//用于声明引用的const都是底层const;
当执行对象的拷贝操作时,常量是顶层const还是底层const区别明显,其中,顶层const不受什么影响;
底层const的限制却不容忽视,当执行对象的拷贝操作时,拷入和拷出的对象必须具有相同的底层const资格,或者当两个对象的数据类型必须能转换,一般来说,非常量可以转换成常量,反之则不行
int *p = p3;//错误:p3包含底层const的定义,而p没有;
p2 = p3;//正确,p2和p3都有顶层const的定义;
p2 = &i;//正确,&i是int *类型,非常量可以转换常量,int * 可以转换呈const int *;
int &i = ci;//错误:普通的int &不能绑定到const int 上
const int &r2 = i;//正确:const int 可以绑定到普通int上
ps:遇到复杂的const和指针组合时,推荐从右往左读,比如int *const p:从右第一个时常量,说明p是个常量,const后面是*,说明这个常量类型是指针,*后面是int,说明这个常量指针指向int类型,
再比如int const* p = const int *p:第一种表明这是个指针,指针指向常量,常量类型是int,说明这是个指向int常量的指针,第二中表明这是个指针,指针指向int类型,指向的int类型是个常量,说明这是个指向int常量的指针,两者一样。