- Rongsen.Com.Cn 版权所有 2008-2010 京ICP备08007000号 京公海网安备11010802026356号 朝阳网安编号:110105199号
- 北京黑客防线网安工作室-黑客防线网安服务器维护基地为您提供专业的
服务器维护
,企业网站维护
,网站维护
服务 - (建议采用1024×768分辨率,以达到最佳视觉效果) Powered by 黑客防线网安 ©2009-2010 www.rongsen.com.cn
作者:黑客防线网安C/C++教程基地 来源:黑客防线网安C/C++教程基地 浏览次数:0 |
1、类的存储空间
在INTEL 32 CPU,VC6环境下,空类的一个实例占一个字节;
虚拟函数表指针占4个字节。
2、虚函数的实现过程
[网上很多讲解, 本文有源代码和部分汇编代码]
3、虚拟析构函数
无论基类的析构函数是否为虚析构函数. 基类的析构函数总是会被自动调用的;
但是, 如果用基类指针去操作一个了派生类对象,
那么在delete这个基类指针时,派生类的析构函数将不会被调用.
4. 补充: 一个C++类本身,在内存里是有信息的, 除了上面 虚函数表, 还有静态成员变量。
VC6下的代码:
// Test.cpp : Defines the entry point for the console application.
//
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
#include <stdio.h>
class Base;
class Derived;
void GFunction(void);
int main(int argc, char* argv[])
{
GFunction();
char a = 127;
a+=1;
printf("new a = %d
", a);
getchar();
return 0;
}
class Base
{
public:
Base::Base()
{
};
virtual Base::~Base()
{
printf("Base deconstruct
");
};
virtual void Fun()
{
};
int a ;
};
class Derived : public Base
{
public:
Derived::Derived()
{
};
virtual Derived::~Derived()
{
printf("Derived deconstruct
");
};
virtual void Fun()
{
};
};
void GFunction(void)
{
printf("Class Base Sizeof =%d
", sizeof(Base));
printf("Class Derived Sizeof =%d
", sizeof(Derived));
Base* pA = (Base*)new Derived;
pA->Fun(); // 虚函数调用
delete pA;
}
pA->Fun()的汇编代码如下:
59: pA->Fun();
0040D75C mov edx,dword ptr [ebp-10h] // edx为pA
0040D75F mov eax,dword ptr [edx] // eax为pA对象的虚表指针pVTable
0040D761 mov esi,esp
0040D763 mov ecx,dword ptr [ebp-10h] // this指针存入ecx
0040D766 call dword ptr [eax+4] // 函数地址:虚表指针+4, 就是虚表中第二项
0040D769 cmp esi,esp
0040D76B call __chkesp (00401b20)
1. 如果成员函数不是虚函数,那么编译的时候,就直接指定了调用函数的入口;
2. 如果是虚函数,那么编译时不直接指定函数入口,而是先在对象的内存空间里取一个值(这个值就是虚函数表的地址,放在对象内存空间的最前面4个字节里)。汇编代码中会有取值的过程;
3. 虚函数表中按顺序存放着虚函数在地址空间中的地址:的第一个DWORD存储的就是第一个虚函数的地址,第二个DWORD存储的就是第二个虚函数的地址;
3. 编译器在编译过程中已经知道你调的那个函数在虚函数表中的序号。汇编代码中会有体现;
4. 在运行时,就能正确找到调用函数的地址,并调用它.
析构函数的一点补充:
在一个项目中,如果有N层派生类,编译器总是保证所有基类的析构函数都被依次调用,但问题是,究竟从那层开始调用呢?对于非虚析构函数,显然是在编译期间就直接确定的,对虚析构函数,在运行时,才能确定是从哪一层开始往下层调用(基类)。
事实上,和一般虚函数一样,运行时,才确定要调用的析构函数,不过有些不同的是,析构函数执行完后,下一条指令就是基类析构函数的CALL指令,一直到最上层为止。
下面是一段debug下的反汇编,其中Base派生自BaseBase.可以看到~Base调用后,会自动调用~BaseBase
virtual Base::~Base() // 基类析构函数
{
00401740 push ebp
00401741 mov ebp,esp
00401743 sub esp,0CCh
00401749 push ebx
0040174A push esi
0040174B push edi
0040174C push ecx
我要申请本站:N点 | 黑客防线官网 | |
专业服务器维护及网站维护手工安全搭建环境,网站安全加固服务。黑客防线网安服务器维护基地招商进行中!QQ:29769479 |