为啥纯虚析构函数需要实现

Posted

技术标签:

【中文标题】为啥纯虚析构函数需要实现【英文标题】:Why a pure virtual destructor needs an implementation为什么纯虚析构函数需要实现 【发布时间】:2014-02-02 06:27:15 【问题描述】:

我知道需要纯虚拟析构函数的情况。我也知道,如果我们不为他们提供实现,它会给我一个链接器错误。我不明白的是为什么在如下所示的代码片段中会出现这种情况:

int main()

    Base * p = new Derived;

这里没有删除,所以没有调用析构函数,因此不需要它的实现(假设它的行为应该像其他已声明但未定义的普通函数,链接器只有在我们调用它们时才会抱怨)......还是我错过了什么?

我需要了解为什么这应该是一个特例?

编辑:基于来自 BoBTFish

的 cmets

这是我的基类和派生类

class Base

public:
    Base()
    virtual ~Base() = 0;
;

class Derived : public Base

;

【问题讨论】:

你没有展示你如何写BaseDerived,但我猜发生的事情是编译器正在为Derived生成析构函数,它将尝试调用析构函数Base,不存在。 它确实可以编译...只有链接器会抱怨...尝试一下..我正在使用 VS2012,我非常有信心这不应该依赖于编译器...一旦我出现错误就会消失给出 ~Base 的实现 @Arun 它编译是因为在编译时它不知道Base::~Base() 函数将在哪里实现。至于Derived::~Derived(),由于你没有定义它,它是自动生成的(相当于~Derived() = default;)。 【参考方案1】:

编译器尝试在给定virtual(纯或非)析构函数的情况下构建虚拟表,但由于找不到实现而报错。

virtual 析构函数不同于其他virtual 函数,因为它们在对象被销毁时被调用,而不管它是否被实现。这需要编译器将它添加到 vf 表中,即使它没有被显式调用,因为派生类析构函数需要它。

学究式地,标准需要实现纯virtual析构函数。

【讨论】:

接受这个答案,因为编译器需要定义 Vf 表是有道理的,并且与其他虚函数一致......所有虚函数似乎都需要一个主体来创建对象,即使它们没有被调用,我刚试了一下.. 不过,我还是不明白为什么这段代码可以编译(只有链接器无法找到对 ~Base() 的引用)。为什么即使派生是抽象的,“新派生”也能工作? @undu: Derived 不是抽象的——它的隐式析构函数覆盖了纯虚拟析构函数。 @MikeSeymour 是的,不是答案的一部分。 我不认为引用语言规则是“迂腐的”。那些规则是上帝。实际的后果只会从他们那里得到。【参考方案2】:

C++11 标准:

12.4 析构函数

第 9 段:

析构函数可以声明为虚拟(10.3)或纯虚拟(10.4); 如果该类的任何对象或任何 在程序中创建派生类时,应定义析构函数。 如果一个类有一个基类 虚拟析构函数,其析构函数(无论是用户声明的还是隐式声明的)都是虚拟的。

【讨论】:

我想他是在问为什么链接器在这种情况下会产生错误,而不是纯粹的虚函数。 @LuchianGrigore 正如我所见,你解释得很好,所以我只添加了 C++ 标准参考:)【参考方案3】:

在这一点上,析构函数与其他虚函数不同,因为它们是特殊的并且在基类中自动调用,没有可能、有用或有意义的方法来阻止它。

[C++11: 12.4/9]:析构函数可以声明为virtual(10.3)或纯virtual(10.4); 如果在程序中创建了该类或任何派生类的任何对象,则应定义析构函数。如果一个类有一个带有虚拟析构函数的基类,那么它的析构函数(无论是用户声明的还是隐式声明的)都是虚拟的。

基础总是被破坏,为此,需要一个基础析构函数定义。相反,其他被覆盖的虚函数根本不会自动调用。因此有特殊情况要求。

struct Base

   virtual ~Base()    = 0;  // invoked no matter what
   virtual void foo() = 0;  // only invoked if `Base::foo()` is called
;

Base::~Base() 
/* void Base::foo()  */

struct Derived : Base

   virtual void foo()  /* Base::foo(); */ 
;

int main()

    std::unique_ptr<Base> ptr(new Derived());

【讨论】:

【参考方案4】:

一个实际的原因是,在几乎所有实现中,析构函数在 vtable 中的虚拟成员函数列表中排在首位。并且实现倾向于在定义第一个虚拟成员函数时定义 vtable 本身。所以,没有析构函数,没有 vtable。 vtable 很关键。

【讨论】:

以上是关于为啥纯虚析构函数需要实现的主要内容,如果未能解决你的问题,请参考以下文章

C++中的纯虚析构函数

关于纯虚析构函数的问题

C++11接口纯虚析构函数

局部抽象类的纯虚析构函数

C++ --- 虚析构和纯虚析构

C++中的各种“虚“-- 虚函数纯虚函数虚继承虚基类虚析构纯虚析构抽象类讲解