为了搞清楚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\test\test.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 pushebp
00401051 mov ebp,esp
00401053 push0FFh
00401055 pushoffset __ehhandler$_main (00410c89)
0040105A mov eax,fs:[00000000]
00401060 pusheax
00401061 mov dword ptr fs:[0],esp
00401068 sub esp,48h
0040106B pushebx
0040106C pushesi
0040106D pushedi
0040106E lea edi,[ebp-54h]
00401071 mov ecx,12h
00401076 mov eax,0CCCCCCCCh
0040107B rep stosdword ptr [edi]
9:CTest aTest;
0040107D lea ecx,[ebp-10h]//这是用来保存aTest的this指针,因为是局部变量所以是保存在[ebp-10h]中
00401080 call@ILT+30(CTest::CTest) (00401023)//调用aTest的构造函数,由编译器自动产生的CALL
00401085 mov dword ptr [ebp-4],0
10: aTest.a(1,2);
0040108C push2
0040108E push1
00401090 lea ecx,[ebp-10h]//把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]//同样是this指针
004010A9 call@ILT+25(CTest::~CTest) (0040101e)//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 pushebp
00401231 mov ebp,esp
00401233 sub esp,44h
00401236 pushebx
00401237 pushesi
00401238 pushedi
00401239 pushecx
0040123A lea edi,[ebp-44h]
0040123D mov ecx,11h
00401242 mov eax,0CCCCCCCCh
00401247 rep stosdword ptr [edi]
00401249 pop ecx
0040124A mov dword ptr [ebp-4],ecx
29: printf("call b");
0040124D pushoffset string "call b" (00422038)
00401252 callprintf (00401830)
00401257 add esp,4
30: b();
0040125A mov ecx,dword ptr [ebp-4] //不要想这里的[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//由于是STDCALLR所以栈是由程序自己来平衡的