C++类机制的实现细节

为了搞清楚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  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] //这是用来保存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  push    2
0040108E  push    1
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  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] //不要想这里的[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所以栈是由程序自己来平衡的

  • 0
    点赞
  • 0
    收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:大白 设计师:CSDN官方博客 返回首页
评论

打赏作者

nightsuns

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值