C++ 虚析构(virtual destructor)原理

注意:本文仅为个人理解,可能有误!

先看一段代码:

#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;
}

上面这段代码,输出:

CBase construct ...
CSon construct ...
CSon destructor ...
CBase destructor ...

大家都能理解,而将virtual ~CBase() 的virtual 去掉, 将输出:

CBase construct ...
CSon construct ...
CBase destructor ...

大家将看到,子类的析构函数没有被调用。

 

那为什么加了virtual就会调用子类的析构函数呢?这是本文的主题

 

首先要理解几个概念:

0,在没有加virtual的时候,这个继承体系没有任何虚函数,所以CSon,CBase均不含虚函数表。

1,子类构造函数/析构函数会自动调用父类的构造/析构函数(编译期决定);

2,编译期的常识:

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中会自动递归调用父类的析构函数,所以全部资源释放完成。

 

关于动态绑定部分,可以参见如下两张图:

1,CBase初始化完毕:

base

2,CSon初始化完毕:

son

posted @ 2014-03-07 17:34  *神气*  阅读(1279)  评论(0编辑  收藏  举报