在日常的谈话中,程序员有时会经常说“常量指针”,尽管他们想表达的意思是“指向常量的指针”。这种观点是不准确的,因为它们完全是两种截然不同的概念。
T *pt = new T; //ptr to T
const T *pct = pt; // ptr to const T
T *const cpt = pt; // const ptr to T
你想让某个东西成为const类型的,那你就昨首先明确它是什么:是指针,还是被指向的对象?或者两个都有?在pct的声明中,指针不是const类型的,但它指向的对象是。也就是说,const修饰符修饰的是T 基类,而不是针对指针。在cpt的案例中,我们声明了一个坚贞不屈、永远不会改变的、指向一个非const 类型的指针,const 做指针 cpt 的小妾,而 T 基类想摸她一下都不可以!
若深究包裹在指针和const 的语法问题,那就一定得给个小提醒:const 不是说离谁近,就是谁的,一切得朝 * 号看齐!尽管潘金莲天天和西门庆在床上“风起云涌”、“你死我活”,但从法律层面讲,她依然是咱大郎哥的媳妇儿!请看下面的这两例,它们是完全一样的:
const T *p1 ; // ptr to const T
T const *p2; // also ptr to const T
众所周知,第一种形式是更传统、更常见的,不过,现在越来越多的C++ 专家建议“识时务”的还是用第二种,为啥子来?因为与第一种相比,第二种形式更不容易被误导,你可以试着倒过来念,即:(*p2)(const) (T)——pointer to const T(用中文说,就是“指向常量类T的指针”)。当然,如果大家心里都明白它们的意思和性质,那么用哪种形式都一样的啦。要特别小心的是,别因为一个在 * 号前后而导致的性质截然不同的错误,请看:
T const *p3; // ptr to const
T *const p4; // const ptr to non-const
看到没?结果多可怕呀,一个是指向常量类的指针,一个是常量指针翘起自己的小鸡鸡指向一个非常量类!
当然,定义一个指向常量类的常量指针也是可以的。
const T *const cpct1 = pt; // everything is const
T const *const cpct2 = pt; // the same with the above
要知道,相比用常量指针,引用就简单的多了:
const T &rct = *pt; // rather than const T *const (而不是const T *const )
T &rt = *pt; // rather than T *const (而不是T *const )
我们能够将一个指向非常量的指针转变成一个指向常量的指针(we are able to convert a pointer to non-const into a pointer to const )。例如,我们可以用T基类的指针pt的值来初始化一个指针(指向常量T),粗略地说它合法,是因为这样干不会出啥乱子(we were able to initialize pct (of type const T *) with the value of pt (of type T *). The reason this is legal is that, speaking nontechnically, nothing bad can happen.)。设想一下当一个非常量对象的地址被拷贝给一个指向const 的指针时将会出现什么结果,就像下面这个图所示:
Figure 3. A pointer to const may refer to a non-const object.
(不好意思,原图片不能复制,所以我自己画了一个)
指向const 的指针pct 是指着一个非const 的T,但是这种情形不会造成任何伤害。事实上,用 const 类型的指针或者引用来指非 const 对象的做法很普遍的:
void aFunc( const T *arg1, const T &arg2 );
//...
T *a = new T;
T b;
aFunc( a, b );
当我们调用函数aFunc时,我们用a 初始化 arg1 ,用b 初始化 arg2。我们没有声明 a 或者 b 是const 指针;但甭管是不是,在函数被调用而工作时,a, b在函数 aFunc 里面会被像 const 类型一样对待。这个技巧很有用。
那么相反的一种转换,哎,也就是把一个指向const 的指针转换成指向非 const 的指针,是非法的,后果将会很危险!(The reverse conversion, from pointer to const to pointer to non-const, is not legal because it would be dangerous)
本例中,pct可能实际上在指向一个被定义为 const 型的对象。设想一下,如果我们能够将一个指向 const 的指针转化为一个指向非 const 的指针,那么本例中的 pt 就可以以此改变 acT 的值呀!
const T acT;
pct = &acT;
pt = pct; // 谢天谢地,在此编译器会通知程序员出错喽!
*pt = aT; // 看到没?这个指向非 const 的指针 pt 竞企图让 aT 赋值给它,以此来实现改变 const 变量 acT !!
C++标准告诉我们类似这种无耻的赋值将会出现不可知的结局;我们不精确地知道结果会发生什么,但肯定不会是什么好下场!当然,我们可以采用一个 cast 进行顺利的转换。
pt = const_cast<T *>(pct); // not an error, but inadvisable(不算错,但不建
// 议这样干)
*pt = aT; // attempt to change const object!
然而,此类赋值行为的结果依然是不可预见的,如果 pt 是指向 acT 那种 const 型变量。