关于c ++:有没有具体的理由使用非虚拟析构函数?

Are there any specific reasons to use non-virtual destructors?

如我所知,任何被指定具有子类的类都应该用虚拟析构函数声明,这样当通过指针访问类实例时,类实例可以被正确地销毁。

但是为什么用非虚拟析构函数声明这样的类是可能的呢?我相信编译器可以决定何时使用虚拟析构函数。那么,这是C++设计的疏忽,还是我错过了什么?


Are there any specific reasons to use non-virtual destructors?

是的,有。

主要归结为性能。不能内联虚拟函数,必须先确定要调用的正确函数(需要运行时信息),然后调用该函数。

在性能敏感的代码中,无代码和"简单"函数调用之间的区别可能会产生差异。与许多语言不同,C++不认为这种差别是微不足道的。

But why it's even possible to declare such class with non-virtual destructor?

因为(对于编译器)很难知道类是否需要虚拟析构函数。

以下情况下需要虚拟析构函数:

  • 在指针上调用delete
  • 通过基类到派生对象

当编译器看到类定义时:

  • 它不知道您打算从这个类派生——毕竟您可以从没有虚拟方法的类派生
  • 但更可怕的是:它不知道您打算在这个类上调用delete

许多人认为多态性需要更新实例,这完全是缺乏想象力:

1
2
3
4
5
6
7
8
9
10
11
12
13
class Base { public: virtual void foo() const = 0; protected: ~Base() {} };

class Derived: public Base {
  public: virtual void foo() const { std::cout <<"Hello, World!
"
; }
};

void print(Base const& b) { b.foo(); }

int main() {
  Derived d;
  print(d);
}

在这种情况下,不需要为虚拟析构函数付费,因为在销毁时不涉及多态性。

最后,这是一个哲学问题。在实用的情况下,C++选择默认的性能和最小服务(主要是RTTI除外)。

关于警告。有两个警告可用于发现问题:

  • -Wnon-virtual-dtor(gcc,clang):当具有虚函数的类没有声明虚析构函数时发出警告,除非基类中的析构函数被设置为protected。这是一个悲观的警告,但至少你不会错过任何东西。

  • -Wdelete-non-virtual-dtor(clang,也移植到gcc):在指向具有虚拟函数但没有虚拟析构函数的类的指针上调用delete时发出警告,除非该类被标记为final。它有0%的假阳性率,但警告"迟到"(可能几次)。


(P)为什么毁灭者不是通过定义的虚拟?(p)(P)Guideline§35;4:A base class destructor should be either public and虚拟,or protected and non-virtual.http://www.gotw.ca/publications/mill18.htm(p)(P)另见:http://www.erata.net/programming/virtual-destructors/(p)(P)可能是双倍的?当你不应该使用虚拟驱逐舰?(p)


(P)你的问题基本上是这样的,"为什么没有C++竞争你的破坏者的力量是虚拟的,如果阶级有任何虚拟成员?"The logic behind this question is that one should use virtual destructors with classes that they intend to derived from.(p)(P)There are many reasons why the C++compiler doesn't try to out-think the programmer.(p)

  • (P)c++is designed on the principle of get what you pay for.如果你想要的东西是虚拟的,你必须要求它。明白吗?一个类别中的每一项功能,如果是虚拟的,都必须明确宣布(不必克服基于阶级的版本)。(p)
  • (P)如果一个有虚拟成员的班级的破坏者是自动制造的,那么,如果你不想做什么,你怎么会选择不做虚拟?c++doesn't have the ability to explicitly declare a non-virtual method.So how would you override this compiler-driven behavior.(p)(P)是否有一个特别有效的案例使用虚拟类与非虚拟驱逐舰?我不知道。也许有一个堕落的案例在哪里。但是,如果你需要它的一些理由,你不可能说它在你的建议下。(p)
  • (P)The question you should really ask yourself is why more compilers don't issue warnings when a class with virtual members doesn't have a virtual destructor.That's what warnings are for,after all.(p)


    (P)Another reason I haven't seen mentioned here are dll boundaries:you want to use the same allocator to free the object that you used to allocate i t.(p)(P)如果这种方法存在于一个DLL中,但当事人的编码瞬息万变,目标与一个直接的DOCX1字体相同,那么当事人的委托人被用来获取该目标的备忘录,但该目标是被隐藏在DLL中的,而DLL则将其指向一个毁灭者,而该毁灭者使用的是该委托人,而DLL是与自由的目标联系在一起的。(p)(P)当客户中的DLL分类不被使用时,问题就消失了,成为DLL的虚拟毁灭者。(p)


    (P)a Non-虚拟驱逐舰seems to make sense,when a class is just non-虚拟after all(note 1).(p)(P)然而,我看不到有任何其他好的用途供非虚拟破坏者使用。(p)(P)And I appreciate that question.非常感兴趣的问题!(p)(P)Edit:(p)(P)说明1在业绩――关键案例中,这可能是一种在没有任何虚拟功能表和图斯的情况下使用分类的方式,而没有任何虚拟的破坏力。(p)(P)For example:think about a EDOCX1 plenic 0 that contains just three floating point values.如果应用程序的故事是他们的生锈,他们可能是故事在紧凑的时尚。(p)(P)如果我们需要一个虚拟函数表,而且如果我们甚至要求存储在Heap(as in Java&Amp;Co.),那么,这些数字将仅仅包含在备忘录中当前的"一些"要素中。(p)(P)Edit 2:(p)(P)我们甚至可能有一个不使用任何虚拟方法的分类方法。(p)(P)Why?(p)(P)因为,即使我们采取了"虚拟"方法,这也可能是一个普通的和更可取的情况,这并不是我们所能想象的唯一情况。(p)(P)As in many details of that language,C++offers you a choice.You can choose one of the provided options,usually you will choose the one that one who else chooses.但有时你不想这样做!(p)(P)在我们的例子中,一个类别的向量3可能会从类别向量中吸进,2但仍然不会有虚拟功能呼叫的顶端。Thought,that example is not very good;)(p)