為了搞清楚VC中類的實現專門寫了一個最簡單的類,用來觀察它的實現過程,代碼如下:
// test.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include "CTest.h"
int main(int argc, char* argv[])
{
CTest aTest;
aTest.a(1,2);
return 0;
}
// CTest.h: interface for the CTest class.
//
//////////////////////////////////////////////////////////////////////
#if !defined(AFX_CTEST_H__2CCCDCFC_6C3A_48BC_9CD0_E7A8E63431D9__INCLUDED_)
#define AFX_CTEST_H__2CCCDCFC_6C3A_48BC_9CD0_E7A8E63431D9__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
class CTest
{
public:
CTest();
virtual ~CTest();
public:
void b();
void a(int one,int two);
};
#endif // !defined(AFX_CTEST_H__2CCCDCFC_6C3A_48BC_9CD0_E7A8E63431D9__INCLUDED_)
// CTest.cpp: implementation of the CTest class.
//
//////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "CTest.h"
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
CTest::CTest()
{
}
CTest::~CTest()
{
}
void CTest::b()
{
printf("b is be called by a");
}
void CTest::a(int one,int two)
{
printf("call b");
b();
}
下面是相應的反匯編代碼:
--- D:myown est est.cpp ------------
1: // test.cpp : Defines the entry point for the console application.
2: //
3:
4: #include "stdafx.h"
5: #include "CTest.h"
6:
7: int main(int argc, char* argv[])
8: {
00401050 push ebp
00401051 mov ebp,esp
00401053 push 0FFh
00401055 push offset __ehhandler$_main (00410c89)
0040105A mov eax,fs:[00000000]
00401060 push eax
00401061 mov dword ptr fs:[0],esp
00401068 sub esp,48h
0040106B push ebx
0040106C push esi
0040106D push edi
0040106E lea edi,[ebp-54h]
00401071 mov ecx,12h
00401076 mov eax,0CCCCCCCCh
0040107B rep stos dword ptr [edi]
9: CTest aTest;
0040107D lea ecx,[ebp-10h] file://這是用來保存aTest的this指針,因為是局部變量所以是保存在[ebp-10h]中
00401080 call @ILT+30(CTest::CTest) (00401023) file://調用aTest的構造函數,由編譯器自動產生的CALL
00401085 mov dword ptr [ebp-4],0
10: aTest.a(1,2);
0040108C push 2
0040108E push 1
00401090 lea ecx,[ebp-10h] file://把aTest的this指針用ecx進行傳遞
00401093 call @ILT+5(CTest::a) (0040100a)
11: return 0;
00401098 mov dword ptr [ebp-14h],0
0040109F mov dword ptr [ebp-4],0FFFFFFFFh
004010A6 lea ecx,[ebp-10h] file://同樣是this指針
004010A9 call @ILT+25(CTest::~CTest) (0040101e) file://aTest的生存周期到了,自動調用析構函數,同樣是由編譯器分析之後自加上去
004010AE mov eax,dword ptr [ebp-14h]
12: }
004010B1 mov ecx,dword ptr [ebp-0Ch]
004010B4 mov dword ptr fs:[0],ecx
004010BB pop edi
004010BC pop esi
004010BD pop ebx
004010BE add esp,54h
004010C1 cmp ebp,esp
004010C3 call __chkesp (00401670)
004010C8 mov esp,ebp
004010CA pop ebp
004010CB ret
下面再來分析一下VC中對函數的調用:
可以看到上面有對三個函數的調用分別為:
00401080 call @ILT+30(CTest::CTest) (00401023)
00401093 call @ILT+5(CTest::a) (0040100a)
004010A9 call @ILT+25(CTest::~CTest) (0040101e)
可以看到他們都跳到了以@ILT為基的一個地址去了,那麼跳過去之後可以看到:
@ILT+0(??_GCTest@@UAEPAXI@Z):
00401005 jmp CTest::`scalar deleting destructor (00401130)
@ILT+5(?a@CTest@@QAEXHH@Z):
0040100A jmp CTest::a (00401230)
@ILT+10(_main):
0040100F jmp main (00401050)
@ILT+15(?b@CTest@@QAEXXZ):
00401014 jmp CTest::b (004011e0)
@ILT+20(??_GCTest@@UAEPAXI@Z):
00401019 jmp CTest::`scalar deleting destructor (00401130)
@ILT+25(??1CTest@@UAE@XZ):
0040101E jmp CTest::~CTest (004011a0)
@ILT+30(??0CTest@@QAE@XZ):
00401023 jmp CTest::CTest (004010f0)
這個@ILT其實就是一個靜態的表,它記錄了一些函數的入口然後跳過去,每個跳轉jmp占一個字節,然後就是一個四字節的內存地址,所以加起為五個字節,這樣就實現了類的機制。
下面再來分析一下,類的成員函數調用另一成員函數的情況:
27: void CTest::a(int one,int two)
28: {
00401230 push ebp
00401231 mov ebp,esp
00401233 sub esp,44h
00401236 push ebx
00401237 push esi
00401238 push edi
00401239 push ecx
0040123A lea edi,[ebp-44h]
0040123D mov ecx,11h
00401242 mov eax,0CCCCCCCCh
00401247 rep stos dword ptr [edi]
00401249 pop ecx
0040124A mov dword ptr [ebp-4],ecx
29: printf("call b");
0040124D push offset string "call b" (00422038)
00401252 call printf (00401830)
00401257 add esp,4
30: b();
0040125A mov ecx,dword ptr [ebp-4] file://不要想這裡的[ebp-4]肯定是this指針,
0040125D call @ILT+15(CTest::b) (00401014) // 又是@ILT靜態表格
31: }
00401262 pop edi
00401263 pop esi
00401264 pop ebx
00401265 add esp,44h
00401268 cmp ebp,esp
0040126A call __chkesp (00401670)
0040126F mov esp,ebp
00401271 pop ebp
00401272 ret 8 file://由於是STDCALLR所以棧是由程序自己來平衡的