为多态基类声明virtual析构函数

先来看一个例子,代码如下:

#include <iostream>

using namespace std;

class Animal
{
public:
    Animal()
    {
        std::cout << "base class's constructor" << std::endl;
    }

    /*virtual*/ ~Animal()
    {
        std::cout << "base class's destory constructor" << std::endl;
    }

    virtual void eat() = 0;
};

class Lion : public Animal
{
public:
    Lion()
    {
        std::cout << "Lion class's destory constructor" << std::endl;
    }

    ~Lion()
    {
        std::cout << "Lion class's destory constructor" << std::endl;
    }

    void eat() override
    {
        std::cout << "Lion eat action" << std::endl;
    }
};

int main()
{
    Animal *pAnimal = new Lion();
    pAnimal->eat();
    delete pAnimal;
    return 0;
}
View Code

输出如下:

base class's constructor
Lion class's destory constructor
Lion eat action
base class's destory constructor

根据打印的日志发现,子类的析构函数居然没被调用!!!

当基类的指针指向派生类的对象的时候,当我们使用完,对其调用delete的时候,其结果将是未有定义——基类成分通常会被销毁,而派生类的析构函数没被执行,于是造成一个诡异的"局部销毁"对象。这可是形成资源泄漏、败坏之数据结构、产生BUG的根源。


消除以上问题的做法很简单:给基类一个virtual析构函数,此后删除派生类对象就会如你想要的那般。

但是,无端地将所有class的析构函数声明为virtual,就像从未声明它们为virtual一样,都是错误的。因为每一个带有virtual函数的class都有一个相应的vbtl(指向虚函数表的指针vtbl),此对象因为vtbl的引入而不再和其他语言(如C)内的相同声明有着一样的结构,因此也就不在可能把它传递至(或接受自)其他语言所写的函数,因此不再具有移植性。

 

任何类只要带有virtual函数都几乎确定应该也有一个virtual析构函数
如果一个类不含virtual函数,通常表示它并不意图被用做一个基类,当类不企图被当做基类时,令其析构函数为virtual往往是个馊主意。

总结:

  • 带有多态性质的基类应该声明一个virtual析构函数。如果类带有任何virtual函数,它就应该拥有一个virtual析构函数。
  • 一个类的设计目的不是作为基类使用,或不是为了具备多态性,就不该声明virtual析构函数。   
  • STL容器都不带virtual析构函数,所以最好别派生它们。

 

posted @ 2017-03-02 09:33  滴水瓦  阅读(565)  评论(0)    收藏  举报