C++虚函数;虚析构函数;类的存储空间
1.class storage 空 room
在INTEL 32 CPU,VC6环境下,class 空的一个实例占用一个字节;
虚函数表指针占用4个字节。
2。虚函数的实现过程
[网上有很多解释,本文有源代码和部分汇编代码]
3。虚拟析构函数
不管基类的析构函数是不是虚拟析构函数,基类的析构函数总会被自动调用;
但是,如果基类指针用于操作派生类对象,
当基类指针被删除时,派生类的析构函数将不会被调用。
4。补充:一个C++类本身在内存中就有信息,除了上面的虚函数表,还有静态成员变量。
VC6下的代码:
/test.cpp:定义控制台应用程序的入口点。
/
# define Win32 _ lean _ and _ mean//从Windows头中排除很少使用的内容
# include
class Base;
类派生的;
void g function(void);
int main(int argc,char * argv[])
{
g function();
char a = 127;
a+= 1;
printf("new a = %d\n ",a);
getchar();
返回0;
}
class Base
{
public:
Base::Base()
{
};
virtual Base::~ Base()
{
printf(" Base解构\ n ");
};
虚拟void Fun()
{
};
int a;
};
类派生:public Base
{
public:
Derived::Derived()
{
};
virtual Derived::~ Derived()
{
printf(" Derived destruct \ n ");
};
虚拟void Fun()
{
};
};
void g function(void)
{
printf(" Class Base Sizeof = % d \ n ",Sizeof(Base));
printf("类派生的Sizeof =%d \n\n ",Sizeof(Derived));
Base * pA =(Base *)new Derived;
pA-> Fun();//虚函数调用
删除pA;
}
pA-> Fun()的汇编代码如下:
59:pA-> Fun();
0040D75CMV EDX,DWORD PTR [EBP-10H]//EDX是pA
0040D75F MOV EAX,DWORD PTR [EDX]//EAX是pA对象的虚拟表指针PVTABLE
0040 d761 MOVESI,Esp
0040d763mov ecx,DWORD PTR[EBP-10H]//此指针存储在ecx
0040d766调用如果成员函数不是虚函数,编译时直接指定调用函数的入口;
2。如果是虚函数,编译时不直接指定函数入口,而是先从对象的memory 空中取一个值(这个值是虚函数表的地址,放在对象的memory 空的前4个字节中)。汇编代码中会有一个取值过程;
3。虚函数表按顺序存储addresses 空中虚函数的地址:第一个DWORD存储第一个虚函数的地址,第二个DWORD存储第二个虚函数的地址;
3。编译器已经知道你在编译时在虚函数表中调优的函数的序列号。它将反映在汇编代码中;
4。在运行时,您可以正确地找到调用函数的地址并调用它。
析构函数的一点补充:
在一个项目中,如果有N层派生类,编译器总是保证依次调用所有基类的析构函数,但问题是,从哪一层开始调用?对于非虚析构函数,很明显它们是在编译时直接确定的。对于虚析构函数,在运行时,可以确定从哪一层调用下一层(基类)。
事实上,就像一般的虚函数一样,要调用的析构函数是在运行时确定的。但是,也有一些不同之处。析构函数执行后,下一条指令就是基类析构函数的call指令,一直到顶层。
下面是debug下的反汇编,其中Base是从BaseBase派生出来的。可以看到~ base被调用后,~ basebase
虚拟基::~ Base()//基类析构函数
{
00401740 push ebp
00401741 move BP,esp
00401743subesp,0 CCH
00401749 push ebx
0040174 a push ESI
000
0040176 c push offset string " Base解构\ n "(445344h)
00401771 call printf(405760h)
00401776 add esp,4
};
00401779movecx,dword ptr[this]
0040177 c call base base::~ base base(4017 a0h)//上基类也紧随其后。
0条评论