你的C ++对象的析构函数何时应该是virtual
?
答案 0 :(得分:47)
这是因为虚方法的原因是你想使用多态。这意味着您将在基类指针上调用一个方法,并且您希望得到最多的实现 - 这是多态性的全部要点。
现在,如果您没有虚拟析构函数,并且通过指向基类的指针调用析构函数,则最终会调用基类析构函数。在这种情况下,您希望多态性也可以在析构函数上工作,例如通过在基类上调用析构函数,您希望最终调用最派生类的析构函数而不是基类。
class A
{
virtual void f() {}
~A() {}
}
class B : public A
{
void f() {}
~B() {}
}
A * thing = new B();
thing->f(); // calls B's f()
delete thing; // calls ~A(), not what you wanted, you wanted ~B()
让〜(()虚拟打开多态
virtual ~A() {}
所以当你现在打电话
delete thing;
~B()将被调用。
当您将类设计为接口时,您将声明虚拟析构函数,例如你希望它能够被扩展或实施。在这种情况下,一个好的做法是使用虚拟方法和虚拟析构函数创建一个接口类(在Java接口的意义上),然后具有具体的实现类。
您可以看到STL类没有虚拟析构函数,因此它们不应该被扩展(例如std :: vector,std :: string ...)。如果扩展std :: vector并通过指针或引用在基类上调用析构函数,则绝对不会调用可能导致内存泄漏的专用类析构函数。
答案 1 :(得分:31)
来自Stroustrup's C++ Style and Technique FAQ:
那么我什么时候应该声明一个析构函数 虚拟?每当班级有 至少一个虚函数。有 虚函数表示a class意味着充当接口 派生类,当它是,一个 派生类的对象可能是 通过指针摧毁 基
有关when your destructor should be virtual on the C++ FAQ的大量其他信息。 (感谢Stobor)
什么是虚拟会员?来自C++ FAQ:
[20.1]什么是“虚拟成员函数”?
从OO的角度来看,它就是 C ++最重要的一个特性: [6.9],[6.10]。
虚拟函数允许派生 用于替换实现的类 由基类提供。该 编译器确保替换是 每当对象进入时总是被调用 问题实际上是派生的 class,即使访问该对象 用基指针而不是a 派生指针。这允许 基类中的算法 在派生类中替换,即使 用户不知道派生的 类。
派生类可以完全 替换(“覆盖”)基类 成员函数或派生类 可以部分取代(“增加”) 基类成员函数。后者 通过派生而完成 类成员函数调用基数 如果需要,可以使用类成员函数。
答案 2 :(得分:5)
我最近得出结论,完全正确的答案是:
准则#4:基类析构函数 应该是公共的和虚拟的, 或受保护和非虚拟。
当然Herb Sutter gives the rationale他的主张。请注意,他确实超出了通常的答案“当有人通过基类指针删除派生类对象时”,并且“如果你的类有任何虚函数,则使你的析构函数为虚拟”。
答案 3 :(得分:3)
如果您(甚至可能)通过基类指针销毁派生类的对象,则需要一个虚拟析构函数。
我采取的方法是,如果我要从一个类AT ALL派生,那么它将有一个虚拟析构函数。在我编写的代码中实际上没有任何情况,其中虚拟析构函数的性能影响很重要,即使它今天实际上并不需要,它可能在将来修改类时最终需要它。
基本上:将虚拟放在所有基类析构函数上,除非你有一个很好的,经过深思熟虑的理由不这样做。
这只是另一条经验法则,但它可以防止你后来犯错误。
答案 4 :(得分:0)
始终
除非我真的关心vtable的存储和性能开销,否则我总是把它变成虚拟的。除非你有一个静态分析工具来验证你的析构函数在正确的情况下是虚拟的,否则不值得犯错并且在需要时不能创建虚拟析构函数。
答案 5 :(得分:-1)
当基类需要进行自己的清理时,基类对象应该有一个虚析构函数。这就是说,如果您在基类中分配了资源,则必须通过声明其析构函数virtual来清理基类,并保证完成此清理(假设您正确编写了清理)。
通常,方法可以在基类中定义为虚拟,这将允许派生类覆盖虚方法,实现自己派生的特定实现。我发现用一个简单的例子可以清楚地证明这一点。假设我们有一个基类'Shape',现在所有派生类可能都需要具有绘制能力。 'Shape'对象不知道如何绘制从它派生的类,所以在'Shape'类中我们定义了一个虚拟绘图函数。 ie(virtual void draw();)。现在在每个基类中,我们可以覆盖此函数,实现特定的绘图代码(即,正方形的绘制方式与圆形不同)。