何时使用虚拟析构函数?

新手上路,请多包涵

我对大多数 OOP 理论有深刻的理解,但让我很困惑的一件事是虚拟析构函数。

我认为析构函数总是被调用,不管是什么,对于链中的每个对象。

你打算什么时候把它们变成虚拟的,为什么?

原文由 Lodle 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 530
2 个回答

当您可能通过指向基类的指针删除派生类的实例时,虚拟析构函数很有用:

 class Base
{
    // some virtual methods
};

class Derived : public Base
{
    ~Derived()
    {
        // Do some important cleanup
    }
};

在这里,您会注意到我没有将 Base 的析构函数声明为 virtual 。现在,让我们看一下以下代码段:

 Base *b = new Derived();
// use b
delete b; // Here's the problem!

Since Base’s destructor is not virtual and b is a Base* pointing to a Derived object, delete b has undefined行为

[在 delete b ]中,如果待删除对象的静态类型与其动态类型不同,则静态类型应为待删除对象的动态类型和 静态 类型的基类 应具有虚拟析构函数或行为未定义

在大多数实现中,对析构函数的调用将像任何非虚拟代码一样被解析,这意味着将调用基类的析构函数而不是派生类的析构函数,从而导致资源泄漏。

总而言之,当要对基类进行多态操作时,请始终制作基类的析构函数 virtual

如果要防止通过基类指针删除实例,可以使基类析构函数受保护且非虚拟;这样做,编译器不会让你在基类指针上调用 delete

您可以在 Herb Sutter 的这篇文章 中了解有关虚拟性和虚拟基类析构函数的更多信息。

原文由 Luc Touraille 发布,翻译遵循 CC BY-SA 4.0 许可协议

我建议: 如果一个类或结构不是 final ,你应该为它定义虚拟析构函数。

我知道这看起来像是一种过度警惕的矫枉过正,成为一个经验法则。但是,这是确保从您的类派生的人在使用基指针删除时不会有 UB 的唯一方法。

Scott Meyers 在下面引用的 Effective C++ 中的建议很好,但不足以确定。

如果一个类有任何虚函数,它应该有一个虚析构函数,并且那些不是设计为基类或不是设计为多态使用的类不应该声明虚析构函数。

例如,在下面的程序中,基类 B 没有任何虚函数,因此按照 Meyer 的说法,您不需要编写虚析构函数。但是,如果您没有,您在下面有 UB:

 #include <iostream>

struct A
{
    ~A()
    {
        std::cout << "A::~A()" << std::endl;
    }
};

struct B
{
};

struct C : public B
{
    A a;
};

int main(int argc, char *argv[])
{
    B *b = new C;
    delete b; // UB, and won't print "A::~A()"
    return 0;
}

原文由 Özgür Murat Sağdıçoğlu 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题