最近为了彻底弄清楚C++的对象模型,编译器又是如何实现多态的,虚表到底是怎样的,所以在这里自己写了点代码来实现和推敲C++的对象模型. 首先定义如下继承体系:源代码
然后构造对象指针,代码如下: int _tmain(int argc, _TCHAR* argv[]) { IVtbl* pVtbl=NULL;
//vt to real1 int iSize = sizeof(VtblReal1); pVtbl = new VtblReal1(); void (__thiscall VtblReal1::* pfn)(void)=&VtblReal1::f2;//成员函数指针声明赋值 //void* pTemp = (void*)(&VtblReal1::f2); if(pVtbl != NULL) { pVtbl->f1(); pVtbl->f2(); ((VtblReal1*)pVtbl->*pfn)();//成员函数指针调用 delete pVtbl; }
//vt to realb pVtbl = new VtblRealB(); if(pVtbl != NULL) { pVtbl->f1(); delete pVtbl; }
//vt to diamond //IVtbl* pVtbl = new VtblDiamond();//转换不明确VtblReal1还是VtblRealB???纠结 VtblReal1* pVtblReal1 = new VtblDiamond(); if(pVtblReal1 != NULL) { pVtblReal1->f1();
VtblRealB* pTempVtbB = dynamic_cast<VtblRealB*>(pVtblReal1); pTempVtbB->f1();
delete pVtblReal1; }
VtblRealB* pVtbRealB = new VtblDiamond(); if(pVtbRealB != NULL) { IVtbl* pVtbl = dynamic_cast<IVtbl*>(pVtbRealB); pVtbl->f1(); delete pVtbRealB; } return 0; } 执行结果如下:
再让我们反汇编调试看看编译器是如何帮我们实现的? 让我们从汇编角度来看一个对象的初始化 // VtblDemo.cpp : 定义控制台应用程序的入口点。 //
#include "stdafx.h" #include "IVtbl.h" #include "VtblBase.h" #include "VtblReal1.h" #include "VtblRealB.h" #include "VtblDiamond.h"
int _tmain(int argc, _TCHAR* argv[]) { 00961690 push ebp 00961691 mov ebp,esp 00961693 push 0FFFFFFFFh 00961695 push offset __ehhandler$_wmain (965468h) 0096169A mov eax,dword ptr fs:[00000000h] 009616A0 push eax 009616A1 sub esp,1DCh //申请堆栈空间 009616A7 push ebx 009616A8 push esi 009616A9 push edi 009616AA lea edi,[ebp-1E8h] 009616B0 mov ecx,77h 009616B5 mov eax,0CCCCCCCCh 009616BA rep stos dword ptr es:[edi] 009616BC mov eax,dword ptr [___security_cookie (96A0A8h)] 009616C1 xor eax,ebp 009616C3 push eax 009616C4 lea eax,[ebp-0Ch] 009616C7 mov dword ptr fs:[00000000h],eax IVtbl* pVtbl=NULL; 009616CD mov dword ptr [ebp-14h],0 //初始化pVtbl指针为NULL
//vt to real1 int iSize = sizeof(VtblReal1); 009616D4 mov dword ptr [ebp-20h],0Ch //对ebp-20h也就是Isize局部变量赋值sizeof(VtblReal1) //?why=0ch=(sizeof(vftable)+sizeof(DWORD m_dwValue)+sizeof(m_BaseVal))==12 pVtbl = new VtblReal1(); 009616DB push 0Ch //sizeof(VtblReal1) 009616DD call operator new (9611E0h) //内部调用malloc来申请堆空间 009616E2 add esp,4 009616E5 mov dword ptr [ebp-140h],eax //将申请的内存地址赋予ebp-140h(pVtbl指针) 009616EB mov dword ptr [ebp-4],0 009616F2 cmp dword ptr [ebp-140h],0 //比较是否为NULL,如果为空则跳转到96170Eh 009616F9 je wmain+7Eh (96170Eh) 009616FB mov ecx,dword ptr [ebp-140h] //将申请的内存地址赋予ecx 00961701 call VtblReal1::VtblReal1 (96112Ch) //调用VtblReal1的构造函数??构造函数做些什么?请查阅如下的VtblReal1构造函数详解 00961706 mov dword ptr [ebp-1E4h],eax //将返回值赋予ebp-1e4h??ebp-140h查看如下的对象初始化研究 0096170C jmp wmain+88h (961718h) 0096170E mov dword ptr [ebp-1E4h],0 00961718 mov eax,dword ptr [ebp-1E4h] 0096171E mov dword ptr [ebp-14Ch],eax 00961724 mov dword ptr [ebp-4],0FFFFFFFFh 0096172B mov ecx,dword ptr [ebp-14Ch] 00961731 mov dword ptr [ebp-14h],ecx void (__thiscall VtblReal1::* pfn)(void)=&VtblReal1::f2;//成员函数指针声明赋值 00961734 mov dword ptr [ebp-2Ch],offset VtblReal1::`vcall'{4}' (9611FEh) //VtblReal1::`vcall'{4}': //00DA11FE jmp VtblReal1::`vcall'{4}' (0DA1C80h) //00DA1C80 mov eax,dword ptr [ecx] //00DA1C82 jmp dword ptr [eax+4] //跳转到ecx+4 VtblReal1::f2()地址
//void* pTemp = (void*)(&VtblReal1::f2); if(pVtbl != NULL) 0096173B cmp dword ptr [ebp-14h],0 0096173F je wmain+128h (9617B8h) { pVtbl->f1(); 00961741 mov eax,dword ptr [ebp-14h] 00961744 mov edx,dword ptr [eax] 00961746 mov esi,esp 00961748 mov ecx,dword ptr [ebp-14h] 0096174B mov eax,dword ptr [edx] //虚表第一个函数地址对应f1函数 0096174D call eax //调用f1 0096174F cmp esi,esp 00961751 call @ILT+400(__RTC_CheckEsp) (961195h) pVtbl->f2(); 00961756 mov eax,dword ptr [ebp-14h] 00961759 mov edx,dword ptr [eax] 0096175B mov esi,esp 0096175D mov ecx,dword ptr [ebp-14h] 00961760 mov eax,dword ptr [edx+4] //虚表地址+4第二个函数地址f2函数 00961763 call eax //调用f2 00961765 cmp esi,esp 00961767 call @ILT+400(__RTC_CheckEsp) (961195h) ((VtblReal1*)pVtbl->*pfn)();//成员函数指针调用 0096176C mov esi,esp 0096176E mov ecx,dword ptr [ebp-14h] 00961771 call dword ptr [ebp-2Ch] //this->f2(),ecx为this指针 00961774 cmp esi,esp 00961776 call @ILT+400(__RTC_CheckEsp) (961195h) delete pVtbl; 0096177B mov eax,dword ptr [ebp-14h] 0096177E mov dword ptr [ebp-128h],eax 00961784 mov ecx,dword ptr [ebp-128h] 0096178A mov dword ptr [ebp-134h],ecx 00961790 cmp dword ptr [ebp-134h],0 00961797 je wmain+11Eh (9617AEh) 00961799 push 1 0096179B mov ecx,dword ptr [ebp-134h] 009617A1 call IVtbl::`scalar deleting destructor' (961064h) //调用析构函数进行资源释放 009617A6 mov dword ptr [ebp-1E4h],eax 009617AC jmp wmain+128h (9617B8h) 009617AE mov dword ptr [ebp-1E4h],0 }
…... //vt to diamond //IVtbl* pVtbl = new VtblDiamond();//转换不明确VtblReal1还是VtblRealB???纠结 VtblReal1* pVtblReal1 = new VtblDiamond(); …... VtblRealB* pVtbRealB = new VtblDiamond(); 00961950 push 38h 00961952 call operator new (9611E0h) 00961957 add esp,4 0096195A mov dword ptr [ebp-170h],eax 00961960 mov dword ptr [ebp-4],3 00961967 cmp dword ptr [ebp-170h],0 0096196E je wmain+2F3h (961983h) 00961970 mov ecx,dword ptr [ebp-170h] 00961976 call VtblDiamond::VtblDiamond (961258h) 0096197B mov dword ptr [ebp-1E4h],eax 00961981 jmp wmain+2FDh (96198Dh) 00961983 mov dword ptr [ebp-1E4h],0 0096198D mov eax,dword ptr [ebp-1E4h] 00961993 mov dword ptr [ebp-17Ch],eax 00961999 mov dword ptr [ebp-4],0FFFFFFFFh 009619A0 cmp dword ptr [ebp-17Ch],0 009619A7 je wmain+32Ah (9619BAh) 009619A9 mov ecx,dword ptr [ebp-17Ch] 009619AF add ecx,0Ch 009619B2 mov dword ptr [ebp-1E8h],ecx 009619B8 jmp wmain+334h (9619C4h) 009619BA mov dword ptr [ebp-1E8h],0 009619C4 mov edx,dword ptr [ebp-1E8h] 009619CA mov dword ptr [ebp-50h],edx if(pVtbRealB != NULL) 009619CD cmp dword ptr [ebp-50h],0 009619D1 je wmain+39Bh (961A2Bh) { IVtbl* pVtbl = dynamic_cast<IVtbl*>(pVtbRealB);//将VtblDiamond类型指针转换为Ivtbl指针... 009619D3 mov eax,dword ptr [ebp-50h] 009619D6 mov dword ptr [pVtbl],eax pVtbl->f1(); 009619D9 mov eax,dword ptr [pVtbl] 009619DC mov edx,dword ptr [eax] 009619DE mov esi,esp 009619E0 mov ecx,dword ptr [pVtbl] 009619E3 mov eax,dword ptr [edx] 009619E5 call eax 009619E7 cmp esi,esp 009619E9 call @ILT+400(__RTC_CheckEsp) (961195h) delete pVtbRealB; 009619EE mov eax,dword ptr [ebp-50h] 009619F1 mov dword ptr [ebp-158h],eax 009619F7 mov ecx,dword ptr [ebp-158h] 009619FD mov dword ptr [ebp-164h],ecx 00961A03 cmp dword ptr [ebp-164h],0 00961A0A je wmain+391h (961A21h) 00961A0C push 1 00961A0E mov ecx,dword ptr [ebp-164h] 00961A14 call VtblRealB::`scalar deleting destructor' (961091h) //请看如下的对象析构过程研究 //在这里请大家实验没有声明virtual的析构函数与声明virtual的析构函数的区别 //在本人的环境中没有使用Virtual声明的析构函数会导致vc调试器提示断言失败 。。。 对象初始化研究
对象析构过程研究
如此基本上已经可以比较清晰的观察出编译器是如何帮助我们来控制一个对象的生老病死了,即一个指针对象的完整生命周期. |
|