关于c ++:虚拟析构函数是继承的吗?

Are virtual destructors inherited?

如果我有一个带有虚拟析构函数的基类。有派生类也要声明虚析构函数吗?

1
2
3
4
5
6
7
8
9
10
class base {
public:
    virtual ~base () {}
};

class derived : base {
public:
    virtual ~derived () {} // 1)
    ~derived () {}  // 2)
};

具体问题:

  • 1)和2)是否相同?2)自动虚拟是因为它的基础还是它"停止"了虚拟?
  • 如果派生的析构函数与之无关,那么它可以被省略吗?
  • 声明派生析构函数的最佳实践是什么?声明它是虚拟的、非虚拟的还是尽可能省略它?

  • 是的,它们是一样的。不声明某个虚拟的派生类不会阻止它成为虚拟的。事实上,如果任何方法(包括析构函数)在基类中是虚拟的,就无法阻止它在派生类中是虚拟的。在>=C++ 11中,您可以使用EDCOX1 OR 0来防止它在派生类中被重写,但这并不妨碍它是虚拟的。
  • 是的,如果派生类中的析构函数与之无关,则可以省略它。不管它是不是虚拟的。
  • 如果可能的话,我会省略它。为了清晰起见,我总是再次为派生类中的虚拟函数使用virtual关键字。人们不必一直沿着继承层次结构往上走,就可以发现一个函数是虚拟的。此外,如果您的类是可复制的或可移动的,而不必声明自己的复制或移动构造函数,则声明任何类型的析构函数(即使您将其定义为default)都将强制您声明复制和移动构造函数和赋值运算符(如果您希望它们成为编译器不再为您放入的构造函数)。
  • 作为第3项的一小部分。注释中指出,如果未声明析构函数,编译器将生成默认的析构函数(仍然是虚拟的)。默认的是一个内联函数。

    内联函数可能会使更多程序暴露在程序其他部分的更改中,并使共享库的二进制兼容性变得棘手。此外,增加的耦合可能会导致在某些类型的变化面前进行大量的重新编译。例如,如果您决定您真的想要一个虚拟析构函数的实现,那么调用它的每一段代码都需要重新编译。但是,如果您已经在类体中声明了它,然后在.cpp文件中定义为空,那么您可以在不重新编译的情况下修改它。

    我个人的选择仍然是尽可能地忽略它。在我看来,它会使代码混乱,编译器有时可以用默认实现而不是空的实现做一些稍微高效的事情。但你可能会受到一些限制,所以这是一个糟糕的选择。


  • 与所有方法一样,析构函数是自动虚拟的。你不能阻止一个方法在C++中是虚拟的(如果它已经被声明为虚拟的,也就是说,Java中没有等价的"最终")。
  • 是的,可以省略。
  • 如果我希望这个类被子类化,我会声明一个虚拟析构函数,不管它是否子类化另一个类,我也更喜欢继续声明方法virtual,即使它不是必需的。如果您决定删除继承,这将使子类保持工作状态。但我想这只是风格问题。

  • 虚成员函数将隐式地使该函数的任何重载都是虚的。

    所以1)中的virtual是"可选的",基类析构函数为virtual会使所有子析构函数也为virtual。


    1 /是2/是的,它将由编译器生成3/在声明它是否为虚拟成员之间的选择应该遵循您对覆盖虚拟成员的约定——imho,这两种方式都有很好的论点,只需选择一个并遵循它。

    如果可能的话,我会省略它,但是有一件事可能会鼓励您声明它:如果您使用编译器生成的,它是隐式内联的。有时您希望避免内联成员(例如动态库)。