没有虚方法的虚析构函数有什么害处吗?

Is there any harm having a virtual destructor without a virtual method?

本文关键字:析构函数 方法 什么      更新时间:2023-10-16

在我的设计过程中,有时我有添加/删除虚拟方法的情况。我所知道的经验法则是,一旦有了虚方法,就应该有虚析构函数。

我的问题:如果我在创建类时直接添加虚拟析构函数(因此即使没有虚拟方法),是否有任何危害?基本的想法是以后不要忘记它。特别是对于n个派生类,我以后不需要在n个地方修改它。

虚函数表的大小有一个很小的开销。也许不值得担心。虚析构函数还将使您的类成为非聚合类、非平凡类、非标准布局类,因此也是非pod类。根据手头的问题,这可能是不希望看到的。

然而,我建议特别设计你的类,要么是多态的,要么不是。如果要以多态方式使用它们,就给它们一个虚拟析构函数。如果没有,就不要。如果你需要改变它,在你需要的时候就去做。

不,使用虚析构函数是完全有意义的,即使你没有任何其他虚方法。

但是,如果内存使用很重要,那么每个字节都要计算,如果没有任何虚拟方法,则可以获得4或8个字节。在我的应用程序中,我有一些类,它们有数百万个实例。在这种情况下,去掉类中的v指针是有意义的。

如果将析构函数从非虚函数更改为虚函数(反之亦然),为什么需要更改派生类,我不完全理解。一旦一个方法是虚的,那么这个方法在所有的派生类中都将是虚的,即使你没有指定虚的。然而,由于样式的原因,建议在派生类中添加virtual,即使不需要。

唯一的害处是您的类和所有派生类将有一个v表,这是大小的边际增加。即使以后决定将基类析构函数设为虚函数,也不需要对派生类进行任何更改。对于包括析构函数的任何方法,只需在基类中使用一次virtual关键字。派生类中的相同方法自动变为虚方法。

作为一种替代方法,你可以让析构函数受保护。这将防止使用基类指针意外调用delete。
class A
{
    protected:
    ~A(){}
};
class B : public A
{};
int main(int argc, char *argv[])
{
    A * p = new B;
    delete p;
}
在我的编译器中,它给出了以下错误

错误C2248: 'A::~A':无法访问在类'A'中声明的受保护成员A .cpp(9):编译器在这里生成了'A::~A'a.p p(6):参见'A'的声明

不要盲目遵守规则。也就是说,要遵守规则,但不要盲目。

真正需要虚析构函数的唯一情况是通过基对象指针删除对象。经验法则概括并简化了这种情况:如果一个对象可以通过它的基对象指针被删除,那么它将被多态地使用;多态对象可能具有虚函数,而具有虚函数的对象可能以多态方式使用;因此,具有虚函数的对象很可能需要虚析构函数。

这一切都很好,规则大多有效,但有一个更重要、更基本的事实很少被提及,部分原因是这些规则确实有效。事实是,有类值对象也有另一种类型的对象,这类对象没有一个好名字,但我称它们为类实体对象。类实体对象的标识与它们的值是分开的,它们使用引用语义,如果没有充分的理由(比如创建一个单独的标识),它们不应该被复制,它们很可能被多态访问,等等。类值对象除了值之外没有标识,它们可以自由复制,不应该被多态使用,等等。它们是如此不同,所以为它们的类设置不同的关键字是值得的!当你设计你的类时,你必须决定它属于哪一类。那么你的问题就解决了。实体有虚析构函数,值没有

使用虚析构函数的唯一原因是可以通过指向基类型的指针删除派生类型的对象。如果这是类的设计所要求的,那么它必须具有虚析构函数,即使它没有任何其他虚函数。如果设计不包括通过指向基的指针进行删除,则不需要虚析构函数,即使它具有虚函数。不管怎样,那些"带着腰带和吊带"的人会告诉你把析构函数设为虚拟的,因为它不会伤害任何东西,而且,你永远不会知道。这不是一个技术原因;这是一个策略选择,以防止不阅读和遵循文档的用户。