多态基类声明virtual析构函数

  • 问题发现?

在维护一份原有代码时,看到类似下面的代码:(模型简化版)

#include<iostream>
using namespace std;

//定义一个纯虚基类
class Base 
{
public:
    Base() 
    {  
        cout<< "Construct Base!"<< endl;
    };
    ~Base()     //NOTE: non-virtual 
    {
        cout<< "Destruct Base!"<< endl;
    };      

    virtual void DoSomething() = 0;
};


//实现派生类
class Derived : public Base
{
public:
    Derived() 
    { 
        cout<< "Construct Derived!" <<endl;
    };
    ~Derived() 
    { 
        cout<< "Destruct Derived!" <<endl; 
    };   

    void DoSomething() 
    { 
        cout<< "Do whatever in Derived class!" <<endl; 
    };
};

这个实现有问题吗? 让我们简单运行一下看看:

int main()
{
    //case1: 使用基类指针指向派生类对象
    Base *pBase =  new Derived;
    pBase->DoSomething();
    delete pBase;
    pBase = NULL;

    //case2: 使用派生类指针指向派生类对象
    Derived* pDerived = new Derived;
    pDerived = dynamic_cast<Base*>(pDerived);
    pDerived ->DoSomething();
    delete pDerived ;  
    pDerived = NULL;

    return 0;
}

下面是运行结果:

这里写图片描述

结果出现了诡异的“局部销毁”现象,case1中的对象在delete 时没有调用派生类Derived的析构函数,这将很可能造成内存资源泄露。

问题出现在Derived 对象被基类Base 指针删除,而目前的基类Base有个non-virtual 析构函数,这将引起一个灾难性结果,因为C++明确指出:当Derived class对象经由一个Base class 指针被删除,而该Base class 带着non-virtual 析构函数时,其结果是未定义的,实际执行时通常发生对象的Derived成分没有被销毁。

  • 怎么解决呢?

给Base class 一个virtual 的析构函数,此后删除Derived class 对象时就按照预期销毁整个对象,包括所有Derived class。可以更改后自行测试一下,此处不再粘贴结果。

像Base class 这样除了析构函数之外通常还有其他virtual函数,此处virtual目的是允许Derived class 的实现得以具体化,在不同的Derived class中有不同的实现代码。任何class 只要带有virtual函数基本都应该明确也应有一个virtual 析构函数。

  • 问题延伸

某些情况下,欲使一个类成为抽象类,但刚好又没有任何纯虚函数。怎么办? 实际上,抽象类的产生也可由纯虚析构函数实现。

抽象类是准备被用做基类的,如上面所讲,基类必须要有一个virtual析构函数,在没有纯虚函数会产生抽象类的情况下,可以借助于声明一个纯虚析构函数实现:

class AbstractBase 
{
public:
  virtual ~AbstractBase () = 0;   // 声明一个纯虚析构函数
};

该AbstractBase 类有一个纯虚函数,是抽象类,而且有一个virtual析构函数,因此不会产生析构函数导致对象释放不全的问题。

除此声明之外,因为要实例化其派生类对象,还必须提供纯虚析构函数的定义, 即:

AbstractBase::~AbstractBase () {} // 纯虚析构函数的定义

而且该定义是必需的,因为虚析构函数工作的方式是:最底层的派生类的析构函数最先被调用,然后各个基类的析构函数被调用。也就是说,即使是抽象类,编译器也要产生对~AbstractBase() 的调用,因此必须要保证为它提供实现函数体,否则,编译连接时就会报错,直到实现该函数体为止,示例代码如下:

class AbstractBase
{ 
public: 
    virtual ~AbstractBase() = 0; 
}; 

class ConcreteDerived :public AbstractBase 
{ 
public: 
    virtual ~ConcreteDerived(){};
}; 

//AbstractBase的纯虚函数实现体,此时AbstractBase仍为抽象类 
AbstractBase::~AbstractBase(){};  //必须提供,否则编译报错
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值