Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/154.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ C++&引用;虚拟函数,但没有虚拟析构函数“;_C++ - Fatal编程技术网

C++ C++&引用;虚拟函数,但没有虚拟析构函数“;

C++ C++&引用;虚拟函数,但没有虚拟析构函数“;,c++,C++,我有一个基类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(_tit

我有一个基类
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会给我一堆警告
类有虚拟函数,但非虚拟析构函数,但仍然可以编译,并且我的程序运行良好。现在我想消除这些恼人的警告,因此我通过添加一个虚拟析构函数来满足编译器,结果是:它不编译,错误如下:

undefined reference to `Media::~Media()`
使析构函数纯虚拟并不能解决问题。
那么出了什么问题呢?

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

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

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

得到警告的原因是,所有将从中派生的类都应该有一个虚拟或受保护(credit@Steve)析构函数,否则通过指向基类的指针删除实例会导致未定义的行为


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

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

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

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

您还可以将析构函数设置为纯虚拟的,并按照上面提到的问题中的说明进行实现


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

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

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

virtual~Media(){}

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

virtual~Media()=默认值;

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

Media::~Media(){}
问题是:在没有析构函数的情况下,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;
}
是的,只有一次

问题是,当您对类型为
Base*
的变量调用
delete
时,将调用
Base::~Base()
方法。如果它是
virtual
,则调用将动态调度到最终方法(基于动态类型),在本例中为
派生::~Derived()
,但如果不是,则从不调用
派生::~Derived()

因此,如果您希望对基类型调用
delete
(或使用智能指针为您执行此操作),则需要在其类定义中添加
virtual~base(){}
。这就是为什么当您创建一个没有
virtual
析构函数的多态类时,gcc会发出警告


注意:时间变了,从那时起,我在Clang中实现了
-Wdelete非虚拟dtor
,并在gcc中进行了复制


-Wnon virtual dtor
对库编写器很有用(正如它在基类上警告的那样),但可能有较高的误报率;另一方面,在调用站点激发,并且具有更低的误报率(通常可以通过添加
final
来消除类的“多态”属性来解决这个问题)。

纯虚拟析构函数有其用途。但即使声明为纯,仍然需要定义。是的,我的回答只是关于错误。在我的回答中,我将添加在类中使用纯虚拟析构函数以及实现它们的方法+1“将从中派生的所有类都应该有一个虚拟析构函数”-或一个受保护的析构函数,这将防止外部人员通过错误的指针删除。这适用于局外人无论如何都不应该直接删除的情况(例如,因为对象是通过返回智能指针的某个工厂创建的)。真不敢相信我必须实现空析构函数。问题解决了,谢谢大家@Crazyffan:如果你的编译器足够新,你也可以说
=default
(C++11特性)仅仅定义空构造函数还不够吗:
virtual~Media(){}
(Jerry Coffin的答案)?@pevik如果你能使用
=default
,你应该;提供特殊成员功能的用户,即使在实践中实现与默认功能完全相同,也会阻止或妨碍其他功能。否。在类声明之后添加此项将在链接期间生成多个定义。定义必须只包含在一个翻译单元中。这仍然不正确。在类声明之后,在头文件中添加它将导致错误。@LuchianGrigore您能告诉我,如果我在.h文件中添加定义,编译器会给出什么样的错误吗?它在视觉上工作得很好Studio@Hiren如果把它放在包含多个C++源的头文件中,就会得到链接错误,因为定义将被复制一次.CPP文件。为什么你要首先要一个纯虚拟构造函数?一个空的虚拟构造函数工作得很好(它相当于相同数量的非空白字符)@LuchianGrigore,这不是我的意思。我给你换个说法:为什么你想让类媒体是抽象的?@drhirsch通常是因为你想禁止它的创建。可能媒体类型的对象没有意义。如果某人只需要禁用“类具有虚拟功能,但没有
Aargh