【C++学习】C++ 虚析构(virtual destructor)浅析
先来看一段简单的代码(main.cpp):
#include <iostream>
using namespace std;
class CBase{
public:
CBase()
{
cout<<"CBase construct ... "<<endl;
}
virtual ~CBase()
{
cout<<"CBase destructor ... "<<endl;
}
};
class CSon : public CBase{
public:
CSon(){
cout<<"CSon construct ... "<<endl;
}
~CSon(){
cout<<"CSon destructor ... "<<endl;
}
};
void test()
{
CBase* pObj = new CSon();
delete pObj;
}
int main(int argc,char* argv[])
{
test();
return 0;
}
运行结果为:
大家都能理解,而将virtual ~CBase() 的virtual 去掉, 将输出:
大家将看到,子类的析构函数没有被调用。
那为什么加了virtual就会调用子类的析构函数呢?这是本文的主题。
首先要理解几个概念:
1,在没有加virtual的时候,这个继承体系没有任何虚函数,所以CSon,CBase均不含虚函数表。
2,子类构造函数/析构函数会自动调用父类的构造/析构函数(编译期决定);
3,编译期的常识:
CBase* pObj = new CSon();
这句相当于:
CSon* pTmp = new CSon();
CBase* pObj = (CBase*)pTmp;
前一句中编译器是直接调用CSon::CSon()
返回位pTmp,pTmp赋值给pObj。因而生成的pObj实际上是CSon对象,但pObj的类型被记录为CBase类型。
delete pObj;
delete时编译器只知道这个pObj是CBase类型 。
情况一,在CBase的析构函数为非virtual时:
编译器在编译delete pObj;
时根据pObj的类型是CBase调用CBase::~CBase(),不会调用子类析构函数。
情况二,在CBase的析构函数为virtual时:
此时继承体系中有一个虚函数表,这个时候,编译器就不会直接调用CBase::~CBase(),而是调用call pObj->pvtable-> vtable[0],又pObj这个对象实际上是CSon对象,所以调用流程如下:
delete pObj; –> call pObj->pvtable-> vtable[0] –> CSon::~CSon()
而在CSon::~CSon中会自动递归调用父类的析构函数,所以全部资源释放完成。
在命令行里通过如下命令来查看CSon对象的内存布局:
cl main.cpp /d1reportSingleClassLayoutCSon
具体操作方法可以查看我之前的文章—–【C++学习】 VS2012 使用命令行选项查看对象的内存布局
其内存布局如下图所示:
可以看到,由于父类的析构函数为virtual,则会有虚表,虚表中子类的析构函数覆盖了父类的析构函数,这个是通过编译器来处理的,因为子类的析构函数名跟父类不一样,编译器做了处理,让其覆盖父类的析构函数。不知道这样理解是否准确,如有问题,欢迎大家指出。