为啥纯虚析构函数需要实现
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
;
【问题讨论】:
你没有展示你如何写Base
和Derived
,但我猜发生的事情是编译器正在为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 很关键。
【讨论】:
以上是关于为啥纯虚析构函数需要实现的主要内容,如果未能解决你的问题,请参考以下文章