我有一个基类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()`
使析构函数纯虚拟并不能解决问题。 出了什么问题?
答案 0 :(得分:19)
您还需要定义虚拟析构函数,而不仅仅是添加它。
//Media.h
class Media{
//....
virtual ~Media() = 0;
};
//Media.cpp
#include "Media.h"
//....
Media::~Media() {};
您收到警告的原因是所有将派生的类都应该具有虚拟或受保护(信用@Steve)析构函数,否则通过指向基类的指针删除实例会导致未定义的行为。
注意您必须为析构函数提供定义,即使它们是纯虚拟的。
答案 1 :(得分:7)
事情是:没有析构函数,GCC给了我一堆警告“类具有虚函数但非虚析构函数”,但仍然编译并且我的程序工作正常
这在Modern 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
是的,只有一次。
问题是,当您对delete
类型的变量调用Base*
时,会调用Base::~Base()
方法。如果是virtual
,则会将调用动态调度到最终方法(基于动态类型),在本例中为Derived::~Derived()
,但如果不是,则Derived::~Derived()
永远不会被叫,因此从未被执行过。
因此,如果您希望在基本类型上调用delete
(或使用为您执行此操作的智能指针),则需要在其类定义中添加virtual ~Base() {}
。这就是gcc在没有virtual
析构函数的情况下创建多态类时警告你的原因。
注意:时间已经改变,从那时起我在Clang中实现了-Wdelete-non-virtual-dtor
,并且它也在gcc中复制了。
-Wnon-virtual-dtor
对于库编写者很有用(因为它在基类上发出警告),但可能具有更高的误报率;另一方面-Wdelete-non-virtual-dtor
在呼叫站点发生火灾,并且具有低得多的误报率(通常可以通过加密final
来消除该类的“多态”属性)。< / em>的
答案 2 :(得分:6)
您应该实现虚拟析构函数,而不是使其成为纯虚拟析构函数。
查看this类似的问题(可能从虚拟析构函数错误的角度来看,而不是警告),以获取更多信息。
编辑:更通用的解决方案,回复LuchianGrigore的评论(谢谢你指出)您还可以将析构函数设置为纯虚拟,并按照上述问题中的指示实现。
在您的类中使用虚拟析构函数应该是为了防止基类的实例化(即,当您没有其他纯虚方法来使类抽象时)。
答案 3 :(得分:4)
您注释掉的是析构函数的纯虚拟声明。这意味着必须在派生类中重写该函数才能实例化该类的对象。
你想要的只是将析构函数定义为虚函数:
virtual ~Media() {}
答案 4 :(得分:0)
首先取消注释声明 尝试在课后声明中添加以下行
Media::~Media(){}