[转载]const_cast

 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。

 

 

posted @ 2013-08-07 10:25  jiayouwyhit  阅读(295)  评论(0编辑  收藏  举报