一、向上类型转换
情形一:
C++ code
Disassembly
1 B obj;
2 lea ecx,[obj]
3 call B::B (11A1104h)
4
5 {
6 A* p = (&obj);
7 lea eax,[obj]
8 mov dword ptr [p],eax
9 p->fun();
10 mov eax,dword ptr [p]
11 mov edx,dword ptr [eax]
12 mov esi,esp
13 mov ecx,dword ptr [p]
14 mov eax,dword ptr [edx]
15 call eax
16 cmp esi,esp
17 call @ILT+365(__RTC_CheckEsp) (11A1172h)
18 }
19
20 {
21 A* p = static_cast< A* >(&obj);
22 lea eax,[obj]
23 mov dword ptr [p],eax
24 p->fun();
25 mov eax,dword ptr [p]
26 mov edx,dword ptr [eax]
27 mov esi,esp
28 mov ecx,dword ptr [p]
29 mov eax,dword ptr [edx]
30 call eax
31 cmp esi,esp
32 call @ILT+365(__RTC_CheckEsp) (11A1172h)
33 }
34
35 {
36 A* p = dynamic_cast< A* >(&obj);
37 lea eax,[obj]
38 mov dword ptr [p],eax
39 p->fun();
40 mov eax,dword ptr [p]
41 mov edx,dword ptr [eax]
42 mov esi,esp
43 mov ecx,dword ptr [p]
44 mov eax,dword ptr [edx]
45 call eax
46 cmp esi,esp
47 call @ILT+365(__RTC_CheckEsp) (11A1172h)
48 }
49
50 {
51 A* p = reinterpret_cast< A* >(&obj);
52 lea eax,[obj]
53 mov dword ptr [p],eax
54 p->fun();
55 mov eax,dword ptr [p]
56 mov edx,dword ptr [eax]
57 mov esi,esp
58 mov ecx,dword ptr [p]
59 mov eax,dword ptr [edx]
60 call eax
61 cmp esi,esp
62 call @ILT+365(__RTC_CheckEsp) (11A1172h)
63 }
看看B:B()的代码,这个编译器自动帮我们加上去的代码,如果没有虚报是不会有的。
1B::B:
2 push ebp
3 mov ebp,esp
4 sub esp,0CCh
5 push ebx
6 push esi
7 push edi
8 push ecx
9 lea edi,[ebp-0CCh]
10 mov ecx,33h
11 mov eax,0CCCCCCCCh
12 rep stos dword ptr es:[edi]
13 pop ecx
14 mov dword ptr [ebp-8],ecx
15 mov ecx,dword ptr [this]
16 call A::A (1321109h)
17 mov eax,dword ptr [this]
18 mov dword ptr [eax],offset B::`vftable' (1325740h)
19 mov eax,dword ptr [this]
20 pop edi
21 pop esi
22 pop ebx
23 add esp,0CCh
24 cmp ebp,esp
25 call @ILT+365(__RTC_CheckEsp) (1321172h)
26 mov esp,ebp
27 pop ebp
28 ret
情形二:
C++ CODE
只是在上文改动一处:去掉A::fun()的声明virtual。
Disassembly对比
左:无virtual声明 右:有virtual声明
结论: 无论哪种cast,向上类型都一样。函数调用只与虚表有关。向上类型转换是最常见的一种转换,常见的大部分代码都是用"向上类型转换+虚函数"这种黄金组合完成一些c++类库,例如MFC,QT,OGRE.这种转换很符合常规,基本不会出错。
二、向下类型转换
情形一:
C++ CODE
A和B的声明同上,只是改变A::fun()是虚函数。
1 A obj;
2
3 {
4 //B* p = (&obj);
5 //p->fun();
6 //error C2440: 'initializing' : cannot convert from 'A *' to 'B *'
7 //Cast from base to derived requires dynamic_cast or static_cast
8 }
9
10 {
11 B* p = static_cast< B* >(&obj);
12 p->fun();
13 }
14
15 {
16 B* p = dynamic_cast< B* >(&obj);
17 p->fun();
18 }
19
20 {
21 B* p = reinterpret_cast< B* >(&obj);
22 p->fun();
23 }
Disassembly
1 A obj;
2 lea ecx,[obj]
3 call A::A (9310FFh)
4
5 {
6 //B* p = (&obj);
7 //p->fun();
8 //error C2440: 'initializing' : cannot convert from 'A *' to 'B *'
9 //Cast from base to derived requires dynamic_cast or static_cast
10 }
11
12 {
13 B* p = static_cast< B* >(&obj);
14 lea eax,[obj]
15 mov dword ptr [p],eax
16 p->fun();
17 mov eax,dword ptr [p]
18 mov edx,dword ptr [eax]
19 mov esi,esp
20 mov ecx,dword ptr [p]
21 mov eax,dword ptr [edx]
22 call eax
23 cmp esi,esp
24 call @ILT+355(__RTC_CheckEsp) (931168h)
25 }
26
27 {
28 B* p = dynamic_cast< B* >(&obj);
29 push 0
30 push offset B `RTTI Type Descriptor' (937014h)
31 push offset A `RTTI Type Descriptor' (937000h)
32 push 0
33 lea eax,[obj]
34 push eax
35 call @ILT+450(___RTDynamicCast) (9311C7h)
36 add esp,14h
37 mov dword ptr [p],eax
38 p->fun();
39 mov eax,dword ptr [p]
40 mov edx,dword ptr [eax]
41 mov esi,esp
42 mov ecx,dword ptr [p]
43 mov eax,dword ptr [edx]
44 call eax
45 cmp esi,esp
46 call @ILT+355(__RTC_CheckEsp) (931168h)
47 }
48
49 {
50 B* p = reinterpret_cast< B* >(&obj);
51 lea eax,[obj]
52 mov dword ptr [p],eax
53 p->fun();
54 mov eax,dword ptr [p]
55 mov edx,dword ptr [eax]
56 mov esi,esp
57 mov ecx,dword ptr [p]
58 mov eax,dword ptr [edx]
59 call eax
60 cmp esi,esp
61 call @ILT+355(__RTC_CheckEsp) (931168h)
62 }
情形二:
C++ CODE
A和B的声明同上,只是改变A::fun()不是虚函数。
A obj;
{
//B* p = (&obj);
//p->fun();
//error C2440: 'initializing' : cannot convert from 'A *' to 'B *'
//Cast from base to derived requires dynamic_cast or static_cast
}
{
B* p = static_cast< B* >(&obj);
p->fun();
}
{
//B* p = dynamic_cast< B* >(&obj);
//p->fun();
//error C2683: 'dynamic_cast' : 'A' is not a polymorphic type
//* 1> */e:\develop\test\testr555\testr555.cpp(7) : see declaration of 'A'
}
{
B* p = reinterpret_cast< B* >(&obj);
p->fun();
}
Disassembly
A obj;
{
//B* p = (&obj);
//p->fun();
//error C2440: 'initializing' : cannot convert from 'A *' to 'B *'
//Cast from base to derived requires dynamic_cast or static_cast
}
{
B* p = static_cast< B* >(&obj);
lea eax,[obj]
mov dword ptr [p],eax
p->fun();
mov ecx,dword ptr [p]
call B::fun (10C1028h)
}
{
//B* p = dynamic_cast< B* >(&obj);
//p->fun();
//error C2683: 'dynamic_cast' : 'A' is not a polymorphic type
//* 1> */e:\develop\test\testr555\testr555.cpp(7) : see declaration of 'A'
}
{
B* p = reinterpret_cast< B* >(&obj);
lea eax,[obj]
mov dword ptr [p],eax
p->fun();
mov ecx,dword ptr [p]
call
总结:
向下类型转换时,强制类型转换都是不行的。你不能作为一个父亲,却想借着儿子的名号玩一把年轻,用儿子的名义的名号去招摇撞骗以为自己是官二代。不过如果你是儿子,你可以将你的名字换成父亲的,去做在父亲名字下可以做的事,例如表明自己是官二代撞死人撞不死人在倒回来继续撞。
当类型没有虚表的时候,你不能进行向下类型的dynamic_cast,这个时候编译器会报错:
//error C2683: 'dynamic_cast' : 'A' is not a polymorphic type.
不过奇怪的是向上类型转换的时候却没这个错。如果有虚表,还是可以转换的。不过结果是0.也就是说在想下类型转换的时候,'dynamic_cast' 是没有意义的。
三、C++类继承之间转换总结
向上类型转换的时候,你闭着眼睛都可以想出结果,各种转换关键字的反汇编其实都是一样的,都是一个直接赋值。写代码的时候写不写无所谓。
向下类型转换的时候,你得注意了。为了效率,你就酌情选择动态还是静态转换吧。一般情况静态就可以了。
没事不要瞎折腾啊,语言转换不好文档化时分析代码逻辑,也容易出错。