【问题标题】:Should the destructor be always virtual when a class is part of a larger framework?当一个类是更大框架的一部分时,析构函数是否应该始终是虚拟的?
【发布时间】:2015-08-04 04:50:17
【问题描述】:

stackoverflow 上有 this question,它提倡 Scott Meyers 的规则,即只有在该类中有虚函数时才将析构函数设为虚拟。

我在一家拥有大型框架的公司工作,在编写代码时还不清楚您的课程将来是否会扩展。在那个时候,也可能无法更改该类(因为它是已发布包的一部分)。

现在想象以下场景:

class A {
public:
  A();
  virtual ~A();
  virtual m();
};

class B : public A {
public:
  B();
  ~B();
};

class C : public B {
public:
  C();
  virtual ~C();
  virtual m();
};

所以我创建了class B,现在它无法更改。 现在class C 已创建,并用作B

B * b = new C();
delete b;

C 的析构函数永远不会被调用,会发生什么,对吧?

在这种情况下: 一个类是否应该总是有一个虚拟析构函数

【问题讨论】:

  • 真正的答案是不要滥用继承来扩展类。改用合成。
  • 对于遗留代码,使用继承通常是修复错误或行为的唯一方法。例如,我的代码将被重复使用而无法更改。

标签: c++


【解决方案1】:

当基类具有虚拟析构函数时,从它继承的所有类都自动具有虚拟析构函数(因此在您的示例中 - B 的析构函数是隐式虚拟的)

如果类打算被扩展/继承,它应该有虚拟析构函数。

【讨论】:

    【解决方案2】:

    正如@Konrad Grochowski 的回答所述,B 的析构函数是隐式虚拟的,因此行为定义明确。在您的示例中,C::~C() 被调用。来自 C++ 规范第 12.4.8 节:

    析构函数可以声明为虚拟(10.3)或纯虚拟(10.4);如果在程序中创建了该类或任何派生类的任何对象,则应定义析构函数。如果一个类有一个带有虚拟析构函数的基类,那么它的析构函数(无论是用户声明的还是隐式声明的)都是虚拟的。

    更相关的问题是,当您的框架基类实际上有一个非虚拟析构函数(我认为这是您所驱动的)并且您的用户从中派生时会发生什么。例如:

    // Your framework:
    class A
    {
    public:
       A();
       ~A(); // non-virtual
    };
    
    // User's class:
    class B : public A
    {
       B();
       virtual ~B(); // virtual
       virtual void UserMethod();
    };
    

    正如本问题 (Non virtual destructor in base class, but virtual destructor in derived class cause segmentation fault) 中所讨论的,这可能会导致您的用户遇到问题。如果您不确定用户是否会从您的类派生,它应该有一个虚拟析构函数,否则可能会出现问题。

    要使用非虚拟析构函数强制执行正确的行为,您可以禁止用户从该类派生,在这种情况下,假设您在框架内正确使用该类,非虚拟析构函数可能是安全的。在 C++11 中,您可以使用 final 来禁止派生。在 C++03 及更低版本中,您可以使用技巧 here 来禁止派生。

    【讨论】:

    • 我们在很多情况下代码中存在无法更改的错误(或不需要的行为)。有时我们通过从它们派生来在更高级别上绕过这些错误。在这种情况下,如果析构函数不是虚拟的,那就太糟糕了。当然,该代码的作者有最好的意图,但无法预见他们的代码将在其中运行的环境。
    • 在C++11中你不需要技巧来禁止派生,它可以表示为final class A{...};
    【解决方案3】:

    Scott Meyers 在他的书“Effective C++: 55 Specific Ways to Improvement Your Programs and Designs (3rd Edition)”中说,如果类有另一个虚函数,则应该声明析构函数virtual。否则只会为virtual destructor创建v-table,这会导致一些额外的资源消耗。

    ✦Polymorphic  base  classes  should  declare  virtual  destructors.  If  a
    class has any virtual functions, it should have a virtual destructor.
    ✦Classes not designed to be base classes or not designed to be used
    polymorphically should not declare virtual destructors.
    

    【讨论】:

      猜你喜欢
      • 2012-02-24
      • 2018-10-23
      • 2011-03-31
      • 2021-08-22
      • 2017-06-02
      • 2012-06-26
      • 2016-12-23
      • 2011-08-02
      • 2010-09-26
      相关资源
      最近更新 更多