C语言的初学者可能会对,常量指针、指向常量的指针、常量指向常量的指针迷糊一阵。但毕竟也就那么几种情况,熟悉了之后就可以理解了。C++中又多了一个叫做引用的东西,其实这几个东西各自使用也挺简单的,但是如果组合到一起那真叫一个奇葩!相信很有经验的C++程序员也会迷糊一阵!下面我们就说说这个奇葩的东西!
情况1: const + pointer
这个就是我们开头说的C中也存在的奇葩组合。看代码先
int val = 10;
const int cval = 11; int *ptr; ptr = &val; // PASS, 将指向no const的指针指向no const变量
ptr = &cval; // Failed, 将指向no const的指针指向const变量 const int * cptr1; // PASS, const作用于pointer指向的类型,指针本身不是const, 可以以后初始化
cptr1 = &val; // PASS, 将指向const的指针指向no const变量 cptr1 = &cval; // PASS, 将指向const的指针指向const变量 int *const cptr2; // Failed, const作用于指针, const类型变量必须给初值! int *const cptr2 = &val; // PASS, 将指向no const的const指针指向的no const变量 int *const cptr2 = &cval; // Failed, 将指向no const的const指针指向的const变量 const int *const cptr2; // Failed, 第二个const作用于指针, const类型变量必须给初值! const int *const cptr2 = &val; // PASS, 将指向const的const指针指向的no const变量 const int *const cptr2 = &cval; // PASS, 将指向const的const指针指向的const变量
其实写的很清楚了,最主要的是要弄清楚两件事情:
第一,在指针类型中有const出现的时候,要弄清楚const修饰的是指针本身还是指针指向的类型。
第二,遵循“权限缩小原则”,在值传递的过程中权限可以不变,也可以变小,但是不能变大。也就是说no const的值可以赋给const变量,但是不能把const值赋值给no const变量。这个理论同样适于其他限定符。
情况2: const + reference
这个组合就是C++中独有的了,看代码先
int val = 10; const int cval = 11; int &ref1 = val; // PASS, no const引用关联no const变量 int &ref1 = cval; // Failed, no const引用关联const变量 int &ref1 = val + 1; // Failed, no const引用关联临时变量 double &ref1 = val; // Failed, no const引用关联临时变量(类型转换产生临时变量) const int &ref2 = val; // PASS, const引用关联no const变量 const int &ref2 = cval; // PASS, const引用关联const变量 const int &ref2 = val + 1; // PASS, const引用关联临时变量 const double &ref2 = val; // PASS, const引用关联临时变量(类型转换产生临时变量)
同样注意两件事儿:
第一, 遵循“权限缩小原则”。可以由no const向const转移,但是不能反过来。
第二, 在有reference存在的时候一定要注意临时变量的问题。C++规定只有const引用才能关联临时变量,非const引用关联临时变量时非法的。除了上面列出的两种产生临时变量的情况之外,在函数返回的时候还可以产生临时变量。但是,坑爹的是VC2010中非const引用也可以关联由一个函数返回产生的临时变量,它竟然不报错!可能是VS做了某种优化吧?在g++中可以正常报出来。临时变量的问题也是个话题,改次再详细说。
情况3: pointer + reference 和 const + pointer + reference
本来打算分开写的,但是发现他们的耦合度太高了,还是写到一块儿吧。先看代码:
{ int val = 10; const int cval = 11; int *ptr = &val; const int *cptr1 = &cval; int * const cptr2 = &val; const int * const cptr3 = &cval; int * &ref1 = ptr; // OK, no const引用关联no const指针对象ptr int * &ref2 = cptr1; // Failed, no const引用关联指向const的指针对象 int * &ref3 = &val; // Failed, no const引用关联临时变量(&val是临时变量哦!) int * &ref4 = cptr2; // Failed, no const引用关联const指针对象 int * const &cref1 = ptr; // OK, const引用关联非const指针对象(const作用于引用) int * const &cref2 = cptr1; // Failed, const引用(指向no const的指针)关联指向const对象的no const指针 int * const &cref3 = cptr2; // OK, const引用(指向no const的指针)关联const指针对象 int * const &cref4 = &val; // OK, const引用关联临时对象(指向no const的指针临时变量) int * const &cref5 = &cval; // Failed, const引用关联临时对象(指向no const的指针临时变量) const int * &cref6 = ptr; // OK, const作用于引用所关联的类型,要求是指向const的指针。向下可以兼容指向非const的指针 const int * &cref7 = cptr1; // OK, 准确匹配 const int * &cref8 = cptr2; // Failed, 所关联类型指针是const指针 const int * &cref9 = &cval; // Failed, 类型准确匹配,但是尝试关联的是一个临时对象。 const int * const &ccref = ptr; // OK, 双const指针引用通吃所有的 const int * const &ccref = cptr1; // OK, 双const指针引用通吃所有的 const int * const &ccref = cptr2; // OK, 双const指针引用通吃所有的 const int * const &ccref = cptr3; // OK, 双const指针引用通吃所有的 const int * const &ccref = &val; // OK, 双const指针引用通吃所有的 const int * const &ccref = &cval; // OK, 双const指针引用通吃所有的 }
总结:
原理其实不难遵循几个关键点就可以了。
1. 确定const的作用对象,引用还是指针指向的类型。
2. 注意临时变量的问题,只有const引用才能关联临时变量的!
3. 记住一个值,作用在它上面的限制可以越来越多,但是不能变少!
关于临时变量的问题,是个big problem,改次再说,而且在C++0x里面加入了右值引用,这个貌似就是专门为临时变量所加入的类型。下次也深入研究一下。Over!