欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

c++析构函数、虚析构函数、纯虚析构函数详解

程序员文章站 2024-03-21 12:44:52
...

我们知道对象在结束其生命周期之前,都会调用析构函数以完成必要的清理工作;派生类调用的析构函数顺序是“先子类,后基类”; 这篇文章用于总结当析构函数是普通析构函数、虚析构函数、纯虚析构函数时,我们使用delete运算符删除一个指针对象时,析构函数会有什么情况发生

普通析构函数

CBase是基类,CDerive是其子类,类源码代码如下:

class CBase
{
public:
    CBase(){}
    //基类析构函数
    ~CBase(){ cout << "CBase Destructor" << endl; } 
private:
    int a;
};

class CDerive:public CBase
{
public:
    CDerive(){}   
    //子类析构函数
    ~CDerive(){ cout << "CDerive Destructor" << endl; }
private:
    int b;
};

测试代码如下:

//case1
CDerive *pDeriveObj = new CDerive();
delete pDeriveObj;

//case2
//基类指针对象可以指向派生类,体现基类和派生类赋值兼容关系(不同类型可以转化和赋值),
//但是pBaseObj只能访问基类成员,不能访问派生类成员
CBase* pBaseObj = new CDerive();
delete pBaseObj; //等价于删除基类对象

测试结果:

/* case 1
先析构子类:CDerive Destructor
后析构基类:CBase Destructor
*/

/* case2
仅析构基类:CBase Destructor
*/

总结:
1. 若delete运算符删除的是子类指针对象,则会调用子类和基类的析构函数;
2. 若析构函数是非虚的,即使基类指针指向的是子类对象,则delete 指针对象时,也仅调用基类的析构函数

虚析构函数

当基类中的析构函数设置为虚函数时,我们在delete 基类指针对象时,能根据实际类型完成对象的清理工作;源码如下:

class CBase
{
public:
    CBase(){}
    //基类虚析构函数
    virtual ~CBase(){ cout << "CBase Destructor" << endl; } 
private:
    int a;
};

class CDerive:public CBase
{
public:
    CDerive(){}   
    //子类虚析构函数
    virtual ~CDerive(){ cout << "CDerive Destructor" << endl; }
private:
    int b;
};

测试代码:

 //指向基类对象
 CBase*pBaseObj_case1 = new CBase();
 delete pBaseObj_case1;

 //指向子类对象
 CBase*pBaseObj_case2 = new CDerive();
 delete pBaseObj_case2;

运行结果:
//case1
CBase Destructor

//case2
CDerive Destructor ->先子类
CBase Destructor ->后基类

总结:
当基类的析构函数为虚函数时,基类指针指向的是子类对象时,使用delete运算符删除指针对象,析构函能够按照“先子类,后基类”的原则完成对象清理;这样在多重继承的类中,能够保证每个类都能够得到正确的清理;比如基类和子类的缓冲区都能被释放;

纯虚析构函数

当基类中有纯虚函数时,基类是不能被实例化的,需要在子类中重写该纯虚函数;对于纯虚析构函数有点特殊,源码如下:

class CBase
{
public:
    CBase(){}
    //基类析构函数
    virtual ~CBase()= 0;
private:
    int a;
};

class CDerive:public CBase
{
public:
    CDerive(){}   
    //子类析构函数
    virtual ~CDerive(){ cout << "CDerive Destructor" << endl; }
private:
    int b;
};

测试代码:

CBase*pBaseObj = new CDerive();
delete pBaseObj;

当我们编译代码时,会发现代码编译不过提示:
“error LNK2019: 无法解析的外部符号 “public: virtual __thiscall CBase::~CBase(void)” ([email protected]@[email protected]),该符号在函数 “public: virtual __thiscall CDerive::~CDerive(void)” ([email protected]@[email protected]) 中被引用”

原因是子类析构时需要调用基类的析构函数,但发现代码中没有实现CBase析构函数,导致编译异常;
解决办法就是我们在CBase类外实现其函数体,而不是在子类中重写,而且~CDerive函数也是虚函数,即使其函数名不同;这就是和其他纯虚函数有特殊的地方;

我们需要在CBase类外增加如下的析构函数:

CBase::~CBase()
{ 
    cout << "CBase Destructor" << endl; 
} 

这样得到的运行结果是:
CDerive Destructor ->先子类
CBase Destructor ->后基类

总结

最好把基类的析构函数声明为虚函数。这将使所有派生类的析构函数自动成为虚函数。这样,如果程序中显式地用了delete运算符准备删除一个对象,而delete运算符的操作对象用了指向派生类对象的基类指针,则系统会调用相应类的析构函数。

专业人员一般都习惯声明虚析构函数,即使基类并不需要析构函数,也显式地定义一个函数体为空的虚析构函数,以保证在撤销动态分配空间时能得到正确的处理。

参考资料:

http://c.biancheng.net/cpp/biancheng/view/247.html

http://blog.csdn.net/yapian8/article/details/46418687