54

Java 和 C# 支持不能使用 finalandsealed关键字作为基类的类的概念。然而,在 C++ 中,没有很好的方法来防止派生一个类,这让类的作者陷入两难境地,每个类都应该有一个虚拟析构函数吗?


编辑:由于 C++11 这不再是真的,你可以指定一个类是final.


一方面,给一个对象一个虚拟析构函数意味着它将有一个vtable,因此每个对象会消耗 4 个(或 64 位机器上的 8 个)额外字节vptr

另一方面,如果后来有人从这个类派生并通过指向基类的指针删除派生类,则程序将是错误定义的(由于没有虚拟析构函数),坦率地说,为每个对象优化一个指针是荒谬的。

在具有虚拟析构函数的抓手上(可以说)表明这种类型旨在以多态方式使用。

有些人认为你需要一个明确的理由不使用虚拟析构函数(这是这个问题的潜台词),而另一些人则说只有当你有理由相信你的类将派生自你时才应该使用它们,你是什么思考?

4

7 回答 7

58

每个抽象类都应该有一个,

  • 受保护的析构函数,或者,
  • 虚拟析构函数。

如果您有一个公共的非虚拟析构函数,那就不好了,因为它允许用户通过该指针删除派生对象。众所周知,这是未定义的行为。

对于抽象类,您已经需要对象中的虚拟表指针,因此使析构函数virtual(据我所知)在空间或运行时性能方面不会产生高成本。它的好处是派生类自动拥有它们的析构函数virtual(参见@Aconcagua 的评论)。当然,你也可以protected virtual为这种情况制作析构函数。

对于不打算通过指向它的指针删除的非抽象类,我认为没有充分的理由使用虚拟析构函数。它会浪费资源,但更重要的是它会给用户一个错误的提示。想想给std::iterator一个虚拟析构函数会有什么奇怪的意义。

于 2008-12-09T19:06:36.300 回答
31

问题真的是,你想强制执行关于你的类应该如何使用的规则吗?为什么?如果一个类没有虚拟析构函数,那么任何使用该类的人都知道它不打算派生自该类,并且如果您尝试使用它会有哪些限制。这还不够好吗?

或者如果有人敢做你没有预料到的事情,你是否需要编译器抛出一个硬错误?

如果您打算让人们从中派生,请给该类一个虚拟析构函数。否则不要,并假设使用您的代码的任何人都足够聪明,可以正确使用您的代码。

于 2008-12-09T19:18:58.647 回答
8

不!仅当通过基类指针删除派生类的对象时,才使用虚拟析构函数。如果您的类不打算在这种情况下用作基础,请不要将析构函数设为虚拟 - 您将发送错误消息。

于 2008-12-09T19:04:09.597 回答
5

查看Herb Sutter 的这篇文章

准则 #4:基类析构函数应该是公共的和虚拟的,或者是受保护的和非虚拟的。

于 2008-12-10T13:08:24.947 回答
2

对于一般性问题,我会“不”。不是每个班级都需要一个。如果您知道永远不应该继承该类,那么就没有必要产生较小的开销。但是,如果有机会,请保持安全并在其中放一个。

于 2008-12-09T18:58:23.977 回答
1

当基类至少包含一个纯虚函数时,它就变成了抽象类。如果 Base 没有虚拟析构函数而 Derived(从 Base 派生)有,那么您可以通过 Derived 对象指针而不是通过 Base 对象指针安全地销毁 Derived 对象。

于 2010-05-12T20:04:36.073 回答
0

我要补充一点,当我在父类或子类中忘记了虚拟时,我曾一度为没有调用析构函数而挠头。我想我现在知道要找那个了。:)

有人可能会争辩说,有时父类在其析构函数中做了一些孩子不应该做的事情……但这可能表明你的继承结构有问题。

于 2008-12-09T19:14:19.827 回答