在來看看C++的虛函數和繼承
1.C++代碼
#include "iostream"
using namespace std;

class C


{
public:
int c;
virtual void display(int s);
};

void C::display(int s)


{
c = 3;
cout<<"this is in C:"<<s<<" "<<c<<endl;
}
class D : public C


{
public:
int d;
void display(int s);
};

void D::display(int s)


{
d = 4;
cout<<"this is in d:"<<s<<" "<<d<<endl;
}

void main()


{
C c;
c.display(sizeof(c));
C *d = (C *)new D;
d->display(sizeof(d));
}
2.匯編代碼
1.debug編譯
.text:00401830 main proc near ; CODE XREF: _mainj
.text:00401830
.text:00401830 var_54 = dword ptr -54h
.text:00401830 var_14 = dword ptr -14h
.text:00401830 var_8 = dword ptr -8
.text:00401830
.text:00401830 push ebp
.text:00401831 mov ebp, esp
.text:00401833 sub esp, 54h
.text:00401836 push ebx
.text:00401837 push esi
.text:00401838 push edi
.text:00401839 lea edi, [ebp+var_54]
.text:0040183C mov ecx, 15h
.text:00401841 mov eax, 0CCCCCCCCh
.text:00401846 rep stosd
.text:00401848 lea ecx, [ebp+var_8]
.text:0040184B call j_C__C
.text:00401850 push 8
.text:00401852 lea ecx, [ebp+var_8]
.text:00401855 call j_C__display
.text:0040185A lea ecx, [ebp+var_14]
.text:0040185D call j_D__D
.text:00401862 push 0Ch
.text:00401864 lea ecx, [ebp+var_14]
.text:00401867 call j_D__display
.text:0040186C pop edi
.text:0040186D pop esi
.text:0040186E pop ebx
.text:0040186F add esp, 54h
.text:00401872 cmp ebp, esp
.text:00401874 call __chkesp
.text:00401879 mov esp, ebp
.text:0040187B pop ebp
.text:0040187C retn
.text:0040187C main endp

在call真正的函數之前有一個call j_C__C,看看它的代碼
.text:00401890 ; Attributes: bp-based frame
.text:00401890
.text:00401890 C__C proc near ; CODE XREF: j_C__Cj
.text:00401890
.text:00401890 var_44 = dword ptr -44h
.text:00401890 var_4 = dword ptr -4
.text:00401890
.text:00401890 push ebp
.text:00401891 mov ebp, esp
.text:00401893 sub esp, 44h
.text:00401896 push ebx
.text:00401897 push esi
.text:00401898 push edi
.text:00401899 push ecx
.text:0040189A lea edi, [ebp+var_44]
.text:0040189D mov ecx, 11h
.text:004018A2 mov eax, 0CCCCCCCCh
.text:004018A7 rep stosd
.text:004018A9 pop ecx
.text:004018AA mov [ebp+var_4], ecx
.text:004018AD mov eax, [ebp+var_4]
.text:004018B0 mov dword ptr [eax], offset ??_7C@@6B@ ; const C::`vftable'
.text:004018B6 mov eax, [ebp+var_4]
.text:004018B9 pop edi
.text:004018BA pop esi
.text:004018BB pop ebx
.text:004018BC mov esp, ebp
.text:004018BE pop ebp
.text:004018BF retn
.text:004018BF C__C endp

原來是獲取,虛函數表的(*^__^*)...嘻嘻
2.release編譯
.text:00401140 ; int __cdecl main(int argc,const char **argv,const char *envp)
.text:00401140 _main proc near ; CODE XREF: start+AFp
.text:00401140
.text:00401140 var_14 = dword ptr -14h
.text:00401140 var_C = dword ptr -0Ch
.text:00401140 argc = dword ptr 4
.text:00401140 argv = dword ptr 8
.text:00401140 envp = dword ptr 0Ch
.text:00401140
.text:00401140 sub esp, 14h
.text:00401143 push 8
.text:00401145 lea ecx, [esp+18h+var_14]
.text:00401149 mov [esp+18h+var_14], offset off_4120EC
.text:00401151 call sub_401000
.text:00401156 push 0Ch
.text:00401158 lea ecx, [esp+18h+var_C]
.text:0040115C mov [esp+18h+var_C], offset off_4120E8
.text:00401164 call sub_4010A0
.text:00401169 add esp, 14h
.text:0040116C retn
.text:0040116C _main endp
release就直接把虛函數表給解釋出來了
3.輸出結果
this is in C:8 3
this is in d:12 4
4.結論
1.虛基類除了變量還有4字節的vftable(在變量前面)
2.debug要用函數解釋vftable,release直接給出
3.經常說的所謂虛函數被覆蓋過程,可以看看class D解釋vftable代碼
.text:004018E9 pop ecx
.text:004018EA mov [ebp+var_4], ecx
.text:004018ED mov ecx, [ebp+var_4]
.text:004018F0 call j_C__C //初始化父類
.text:004018F5 mov eax, [ebp+var_4]
.text:004018F8 mov dword ptr [eax], offset ??_7D@@6B@ ; const D::`vftable'
.text:004018FE mov eax, [ebp+var_4] // 將寫自己的表

從而可見,VC的編譯器在編譯的時候,沒有覆蓋的概念,只是編譯的時候根據有需要的將虛函數表生成不同的幾個而已。是哪個就用哪個表。
note:
雖然new出來的是一個子類對象,但是由于它付給了一個父類的類型,所以只能引用父類的成員。這就出現了一個奇怪的現象,在繼承的時候已經將父類的成員繼承到了子類的對象里面,而用vc查看的時候會發現這一點,奇怪的是由于上面的原因他將把子類的成員忽略掉。即在本來應該是子類成員的地方,還是父類成員的名稱。雖然查看內存,已經發現子類的成員確實存在。而我們可以把這個看作是類型被沒有warning的縮小了!
posted on 2008-03-02 22:12
margin 閱讀(793)
評論(3) 編輯 收藏 引用 所屬分類:
C/C++ 、
逆向工程