一、向上類型轉換
情形一:
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()的代碼,這個編譯器自動幫我們加上去的代碼,如果沒有虛報是不會有的。
1
B::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++類繼承之間轉換總結
向上類型轉換的時候,你閉著眼睛都可以想出結果,各種轉換關鍵字的反匯編其實都是一樣的,都是一個直接賦值。寫代碼的時候寫不寫無所謂。
向下類型轉換的時候,你得注意了。為了效率,你就酌情選擇動態還是靜態轉換吧。一般情況靜態就可以了。
沒事不要瞎折騰啊,語言轉換不好文檔化時分析代碼邏輯,也容易出錯。