纯抽象的虚拟析构函数 class

virtual destructor for pure abstract class

根据我发现的 here 和 Whosebug 上的其他链接,如果我们计划以多态方式使用它,我们应该始终在基 class 中定义一个虚拟析构函数。我想知道这条规则是否有例外。

我看到生产代码没有为纯抽象基 classes 定义虚拟析构函数,在 cppcon 2014 视频之一 Accept no visitor 中,10:06 定义了 BoolExp 结构是纯抽象 class 并且没有虚拟析构函数。

所以对于像这样定义的纯抽象class

  class Base {
      public:
         virtual foo() = 0;
         virtual bar() = 0;
     }

我的问题是我们绝对必须为 "Base" class 定义一个虚拟析构函数,即使它确实有任何数据成员?虚析构函数规则有没有例外?

提前致谢。

最好的, RG

使用指向具有非虚拟析构函数的基 class 的指针删除派生 class 对象会导致未定义的行为。

否则你可以没有虚拟析构函数。

如果您从不通过指向基 class 的指针删除对象,则该规则的例外情况。在那种情况下,基础 class 析构函数不需要是 virtual.

但是如果你曾经通过基class指针删除一个对象,那么基class析构函数必须virtual 或者你的程序有未定义的行为。

is it absolutely must that we define a virtual destructor for "Base" class, even though it does have any data members? Are there any exceptions to the virtual destructor rule?

这不是必须。这是一个可以防止错误的好习惯。

如果您决定创建一个没有虚拟析构函数的基础class,那么作为开发者,您有责任始终确保派生对象作为正确的类型或作为确实具有虚拟析构函数的基类型删除。

My question is it absolutely must that we define a virtual destructor for "Base" class, even though it does have any data members?

严格来说,没有

但是,基class是否有任何成员变量是无关紧要的。如果使用指向基 class 的指针调用析构函数,无论基 class 是否有任何成员变量,您的代码都有未定义的行为。

Are there any exceptions to the virtual destructor rule?

如果您能够管理派生 classes 的生命周期,使得对 delete 对象的调用是通过派生 class 指针完成的,那么您就不需要调用未定义的行为并且您的代码将表现良好,假设您的代码库中的其他一切都井井有条。

My question is it absolutely must that we define a virtual destructor for "Base" class, even though it does have any data members?

视情况而定。如果你有这样的案例

base * foo = new child(stuff);
// doing stuff
delete foo;

那么你绝对必须有一个虚拟析构函数。没有它,你永远不会破坏 child 部分。

如果你遇到这样的情况

child * foo = new child(stuff);
// doing stuff
delete foo;

那么你不需要虚拟析构函数,因为将调用 child

所以规则是如果你多态删除,你需要一个多态(虚拟)析构函数,如果不是,那么你不需要

我想对描述通过基础对象删除的正确答案进行重要的(至少在我看来)实际修改。

特别是,如果通过

分配的 std::shared_ptr<Base> 管理对象生命周期,则非虚拟地调用析构函数
std::shared_ptr<Base> sptr = std::make_shared<Derived>(args);

we should always define a virtual destructor in the base class if we plan to use it polymorphically.

如果我们计划通过基 class 以多态方式删除它,则应始终在基 class 中定义虚拟析构函数。

现在,一个问题是 "I don't intend to" 不安全;你应该让它不可能。

使析构函数成为虚拟的(且为空),使其受保护(且为空)。受保护的析构函数使多态删除不太可能(它可以被绕过,但只能通过非常疯狂的方式)。

除此之外,您必须小心。这就是为什么要警惕从(比如)std vector 继承的原因之一。