42

我有一个基类Media和几个派生类,即DVD,Book等...基类写成:

class Media{
    private:
        int id;
        string title;
        int year;
    public:
        Media(){ id = year = 0; title = ""; }
        Media(int _id, string _title, int _year): id(_id), title(_title), year(_year) {}
//      virtual ~Media() = 0;
        void changeID(int newID){ id = newID; }
        virtual void print(ostream &out);
};

问题是:没有析构函数,GCC 给了我一堆警告class has virtual functions but non-virtual destructor,但仍然可以编译,我的程序运行良好。现在我想摆脱那些烦人的警告,所以我通过添加一个虚拟析构函数来满足编译器,结果是:它没有编译,错误:

undefined reference to `Media::~Media()`

使析构函数纯虚拟并不能解决问题。那么出了什么问题呢?

4

5 回答 5

48

您还需要定义虚拟析构函数,而不仅仅是添加它。

//Media.h
class Media{
    //....
    virtual ~Media() = 0;
};

//Media.cpp
#include "Media.h"
//....
Media::~Media() {};

您收到警告的原因是所有将派生的类都应具有虚拟或受保护(信用@Steve)析构函数,否则通过指向基类的指针删除实例会导致未定义的行为。

请注意,您必须为析构函数提供定义,即使它们是纯虚拟的。

于 2012-04-05T08:03:34.623 回答
24

问题是:没有析构函数,GCC 给了我一堆警告“类具有虚函数但非虚析构函数”,但仍然可以编译并且我的程序运行良好

这在现代 C++ 中是一个令人讨厌的警告,但在旧的对象样式 C++ 中它通常是正确的。

问题在于您的对象被破坏的方式。一个简单的测试:

#include <iostream>

class Base {};
class Derived: public Base { public: ~Derived() { std::cout << "Aargh\n"; } };

int main() {
  Base* b = new Derived();
  Derived* d = new Derived();

  delete d;
  delete b;
}

这打印:

Aargh

是的,只有一次。

问题是,当您调用deletetype 的变量时Base*,会调用该Base::~Base()方法。如果是virtual,则调用将动态分派到最终方法(基于动态类型),在这种情况下Derived::~Derived(),但如果不是,则Derived::~Derived()永远不会调用,因此永远不会执行。

因此,如果您希望delete在基本类型上调用(或使用智能指针),那么您需要添加virtual ~Base() {}它们的类定义。这就是为什么当你创建一个没有virtual析构函数的多态类时 gcc 会警告你。


注意:时间变了,从那以后我-Wdelete-non-virtual-dtor在 Clang 中实现,它也在 gcc 中复制。

-Wnon-virtual-dtor对库编写者有用(因为它在基类上发出警告),但可能有更高的误报率;另一方面-Wdelete-non-virtual-dtor,在调用站点触发,并且误报率要低得多(您通常可以通过final删除类的“多态”属性来解决这个问题)。

于 2012-04-05T10:02:06.237 回答
9

您注释掉的是析构函数的纯虚拟声明。这意味着必须在派生类中重写该函数才能实例化该类的对象。

您想要的只是将析构函数定义为虚函数:

virtual ~Media() {}

在 C++ 11 或更高版本中,通常最好将其定义为默认值,而不是使用空主体:

virtual ~Media() = default;
于 2012-04-05T08:05:58.850 回答
6

您应该实现虚拟析构函数,而不是使其成为纯虚拟的。

查看这个类似的问题(从虚拟析构函数错误的角度来看可能相同,而不是警告)以获取更多信息。

编辑:更通用的解决方案,回复 LuchianGrigore 的评论(感谢您指出)

您还可以将析构函数设为纯虚拟并按照上述问题中的说明实现。

在你的类中使用虚拟析构函数应该是为了防止基类的实例化(即当你没有其他纯虚拟方法来使类抽象时)。

于 2012-04-05T08:04:56.277 回答
0

先取消注释声明,然后尝试在类声明后添加以下行

Media::~Media(){}
于 2012-04-05T08:08:01.407 回答