可能重复:
何时使用虚拟析构函数?
你的 C++ 对象的析构函数应该是什么时候virtual
?
这是因为虚方法的原因是你想使用多态性。这意味着您将在基类指针上调用一个方法,并且您想要最派生的实现——这就是多态性的全部意义所在。
现在,如果您没有虚拟析构函数,并且通过指向基类的指针调用析构函数,您最终会调用基类析构函数。在这种情况下,您希望多态性也适用于您的析构函数,例如,通过在您的基类上调用析构函数,您希望最终调用您的最派生类的析构函数而不是您的基类。
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()
拥有 ~A() virtual 开启多态性
virtual ~A() {}
所以当你现在打电话时
delete thing;
~B() 将被调用。
当您将类设计为接口时,您将声明虚拟析构函数,例如您希望它被扩展或实现。在这种情况下,一个好的做法是拥有一个带有虚拟方法和虚拟析构函数的接口类(在 Java 接口的意义上),然后拥有具体的实现类。
您可以看到 STL 类没有虚拟析构函数,因此它们不应该被扩展(例如 std::vector、std::string ...)。如果扩展 std::vector 并通过指针或引用在基类上调用析构函数,则绝对不会调用可能导致内存泄漏的专用类析构函数。
来自Stroustrup 的 C++ 风格和技术常见问题解答:
那么我什么时候应该声明一个虚拟的析构函数呢?每当类至少有一个虚函数时。具有虚函数表明一个类旨在充当派生类的接口,并且当它是,派生类的对象可以通过指向基类的指针被销毁。
C++ FAQ 上有很多关于你的析构函数何时应该是虚拟的附加信息。(感谢斯托伯)
什么是虚拟会员?从C++ 常见问题解答:
[20.1] 什么是“虚拟成员函数”?
从 OO 的角度来看,它是 C++ 最重要的一个特性:[6.9]、[6.10]。
虚函数允许派生类替换基类提供的实现。编译器确保只要有问题的对象实际上属于派生类,就总是调用替换,即使该对象是通过基指针而不是派生指针访问的。这允许在派生类中替换基类中的算法,即使用户不知道派生类。
派生类可以完全替换(“覆盖”)基类成员函数,或者派生类可以部分替换(“增强”)基类成员函数。如果需要,后者是通过让派生类成员函数调用基类成员函数来完成的。
我最近得出结论,完全正确的答案是:
准则 #4:基类析构函数应该是公共的和虚拟的,或者是受保护的和非虚拟的。
当然,赫伯·萨特给出了他的主张的理由。请注意,他确实超出了通常的答案“当有人通过基类指针删除派生类对象时”和“如果你的类有任何虚函数,则使你的析构函数成为虚拟”。
如果您将(甚至可能)通过基类指针销毁派生类的对象,则需要一个虚拟析构函数。
我采取的方法是,如果我要从一个类派生,那么它应该有一个虚拟析构函数。在我编写的代码中,实际上没有任何情况下虚拟析构函数的性能影响很重要,即使今天实际上不需要它,将来修改类时也可能最终需要它。
基本上:将 virtual 放在所有基类析构函数上,除非你有一个很好的、经过深思熟虑的理由不这样做。
这只是另一条经验法则,但它可以防止你以后犯错误。
总是。
除非我真的关心 vtable 的存储和性能开销,否则我总是将其设为虚拟。除非您有一个静态分析工具来验证您的析构函数在正确的情况下是虚拟的,否则不值得犯错误并且在需要时未能创建虚拟析构函数。
当基类需要进行自己的清理时,基类对象应该有一个虚拟析构函数。也就是说,如果您在基类中分配了资源,则基类有必要进行清理,通过将其析构函数声明为虚拟,您可以保证此清理将完成(假设您正确编写了清理)。
一般来说,可以在基类中定义虚拟方法,这将允许派生类覆盖虚拟方法,实现自己的派生特定实现。我发现用一个简单的例子可以最清楚地说明这一点。假设我们有一个基类“Shape”,现在可能要求所有派生类都具有绘图能力。'Shape' 对象将不知道如何绘制从它派生的类,因此在 'Shape' 类中我们定义了一个虚拟绘制函数。即(虚拟 void draw();)。现在,在每个基类中,我们可以覆盖这个函数,实现特定的绘图代码(即,正方形与圆形的绘制方式不同)。