1. 一个经典实例
1 /* 2 用法:const_cast<type_id> (expression) 3 该运算符用来修改类型的const或volatile属性。除了const 或volatile修饰之外, type_id和expression的类型是一样的。 4 一、常量指针被转化成非常量指针,并且仍然指向原来的对象; 5 二、常量引用被转换成非常量引用,并且仍然指向原来的对象; 6 三、常量对象被转换成非常量对象。 7 type_id 必须为指针或引用 8 */ 9 class B 10 { 11 public: 12 int m_iNum; 13 B():m_iNum(50) {} 14 }; 15 16 void foo() 17 { 18 const B *b1 = new B(); 19 //b1->m_iNum = 100; //compile error 20 B *b2 = const_cast<B*>(b1); 21 b2->m_iNum = 200; 22 cout<<"b1: "<< b1->m_iNum <<endl; 23 cout<<"b2: "<< b2->m_iNum <<endl; 24 cout<<endl; 25 const B b3; 26 //b3.m_iNum = 100; //compile error 27 B b4 = const_cast<B&>(b3);//b4 is another object 28 b4.m_iNum = 200; 29 cout<<"b3: "<<b3.m_iNum <<endl; 30 cout<<"b4: "<<b4.m_iNum <<endl; 31 cout<<endl; 32 const B b5; 33 //b5.m_iNum = 100; //compile error 34 B &b6 = const_cast<B&>(b5); 35 b6.m_iNum = 200; 36 cout<<"b5: "<<b5.m_iNum <<endl; 37 cout<<"b6: "<<b6.m_iNum <<endl; 38 cout << endl; 39 // force to convert 40 const int x = 50; 41 int* y = (int *)(&x);// same address, but the content is different 42 *y = 200; 43 cout << "x: "<<x<<" address: "<<&x<<endl; 44 cout << "*y: "<<*y<<" address: "<<y<<endl; 45 cout<<endl; 46 // int 47 const int xx = 50; 48 int* yy = const_cast<int *> (&xx);// same address, but the content is different 49 *yy = 200; 50 cout << "xx: "<<xx<<" address: "<<&xx<<endl; 51 cout << "*yy: "<<*yy<<" address: "<<yy<<endl; 52 cout<<endl; 53 // int 54 const int xxx = 50; 55 int yyy = const_cast<int&> (xxx);// another int 56 yyy = 200; 57 cout << "xxx: "<<xxx<<" address: "<<&xxx<<endl; 58 cout << "yyy: "<<yyy<<" address: "<<&yyy<<endl; 59 } 60 61 int _tmain(int argc, char* argv[]) 62 { 63 foo(); 64 return 0; 65 }
result:
b1: 200
b2: 200
b3: 50
b4: 200
b5: 200
b6: 200
x: 50 address: 002CF880
*y: 200 address: 002CF880
xx: 50 address: 002CF884
*yy: 200 address: 002CF884
xxx: 50 address: 002CF88C
yyy: 200 address: 002CF888
可以改变const 自定义类的成员变量,但是对于内置数据类型,却表现未定义行为。
2. 关于为啥会出现同一个地址,其值却不一样的情况
Program: #include <iostream> using namespace std; int main() { const int a = 1; int *p = const_cast<int*>(&a); *p = 2; cout << “value a=” << a << endl; cout << “value *p=” << *p << endl; cout << “address a=” << &a << endl; cout << “address p=” << p << endl; return 0; }
这个程序,从语法上看,没有任何问题(当然,这种用法不提倡 deprecated )。但是输出结果却是这样的:
value a=1 value *p=2 address a=0xbfe9efb4 address p=0xbfe9efb4(depends on machine)
奇怪了,a和p既然指向同一块地址,为什么输出的内容却不一样呢?
在调试的时候发现,在运行完*p=2时,调试器打出的a的地址和p是一样的,而且a和*p的内容都是2。但为什么输出时候会a=1呢?
还是看汇编代码吧,相关部分:
subl $8, %esp pushl $_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_ subl $12, %esp //将直接数1压栈 pushl $1 //栈顶指针减12 subl $12, %esp pushl $.LC0 pushl $_ZSt4cout .LCFI7: //这句是”value a=”这个字符串的输出函数,char call _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc //栈顶指针增加20 addl $20, %esp pushl %eax //这句是a的值输出,int call _ZNSolsEi addl $20, %esp pushl %eax .LCFI8: //这句是输出的endl call _ZNSolsEPFRSoS_E addl $16, %esp
看完之后,有人会问了:怎么没看到a在哪里输出的啊,那么请你注意上面“栈顶指针增加20”这个注释,增加20之后它到哪里了?注意上面就会发现,前面有两次压栈,指针减4*2=8,一次指针减12,然后再加上20,这时候栈顶指针就到了“将直接数1压栈”下面了,显然进入输出函数后,它会从栈中找输出某个值,这里显然是数字1。
这就是为什么会输出a=1了,因为编译器根本没去找a的值,因为a是const int型,编译器认为它的内容不会变,所以干吗非得去内存中找啊,多麻烦,直接把值放到code里输出不就行了!
从某种意义上来说,const 相当于宏定义,但是程序为变量分配了存储空间,这样就可以进行类型检查。所以说很多 C++ 书籍作者提倡用 const 来代替 define,而且反对对 const 做 cast。就算要做 cast,也请尽量使用 C++ 的 cast 而不是 C 的类型转换。
恶劣的编程风格会带来不可预测的后果。类型转换之类的C留下来的习惯在编写高效率的程序时候是很有用的,但一定要确保知道采用这种方式会产生什么结果再去使用。对于那些 undefined 和 deprecated 的东西可以去利用,但是脑子中一定要有 idea。