纯虚抽象类的虚析构函数

5

根据我在这里以及StackOverflow上的其他链接上发现的信息,如果我们计划通过多态使用基类,则应该始终在基类中定义虚析构函数。我想知道是否有例外。

我看过一些生产代码,其中纯虚基类没有定义虚析构函数,在cppcon 2014的视频Accept no visitor中,大约在10:06处,定义了BoolExp结构体作为一个纯抽象类,并且没有虚析构函数。

因此,对于像这样定义的纯抽象类:

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

我的问题是,即使“Base”类没有任何数据成员,我们是否一定要为其定义虚拟析构函数?是否有任何虚拟析构函数规则的例外情况?

提前致谢。

最好的祝福, RG


3
可以实现,但不建议这么做。如果对象的生命周期不是由指向基类的指针来管理,那么这个操作是不必要的,但我仍然会这么做。 - Marek R
如果你要进行多态的 删除 操作,那么你需要一个虚析构函数。请参考 https://dev59.com/IHRB5IYBdhLWcg3w77on - Fred Larson
2
可能是为了节省空间而省略了幻灯片上的内容。 - Brian Bi
嗯,那一定是这样。感谢你的帮助。 - DDG
7个回答

6

我的问题是,即使"Base"类没有任何数据成员,我们是否绝对必须定义虚析构函数?

这取决于具体情况。如果你的情况像下面这样:

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

如果你的情况符合以下条件,那么你绝对需要一个虚析构函数。如果没有虚析构函数,你永远也无法销毁child部分。

例如:

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

如果您没有进行多态删除,那么您不需要虚析构函数,因为会调用 child 的析构函数。

所以规则是:如果您进行了多态删除,需要使用多态(虚)析构函数;否则,不需要使用。


1

如果您从未通过指向基类的指针删除对象,则此规则的例外情况是。在这种情况下,基类析构函数不需要是 virtual

但是,如果您通过基类指针删除对象,则基类析构函数必须是 virtual,否则您的程序将具有未定义行为。


1
我的问题是,即使“Base”类没有任何数据成员,我们是否绝对必须为其定义虚析构函数?
严格来说,不是必须的。然而,基类是否有成员变量并不相关。如果使用指向基类的指针调用析构函数,则无论基类是否具有任何成员变量,您的代码都会产生未定义的行为。
有没有虚析构规则的例外情况?
如果您能够以这样一种方式管理派生类的生命周期,即通过派生类指针调用delete对象,则不会调用未定义的行为,并且您的代码将表现良好,假设您的代码库中的其他内容也很好。

1

如果我们打算通过基类对其进行多态删除,我们应始终在基类中定义虚拟析构函数。

如果我们计划通过基类对其进行多态删除,则应始终在基类中定义虚拟析构函数。

现在,一个问题是“我不想这样做”的安全性不够;您应该使它不可能发生。

让析构函数为虚拟(且为空),或者将其设置为受保护的(且为空)。受保护的析构函数使多态删除不太可能发生(它可以被绕过,但只能通过相当不可思议的方式)。

除此之外,您必须小心。这就是为什么从(例如)std vector继承会让人谨慎的原因之一。


0

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

否则,您可以不需要虚析构函数。


0
“即使“Base”类没有任何数据成员,我们是否绝对必须定义虚析构函数?虚析构函数规则有例外吗?”
这不是“必须”的。这是一个好习惯,可以防止错误。
如果您决定创建一个没有虚析构函数的基类,那么作为开发人员,您有责任始终确保派生对象被删除为正确类型或作为具有虚析构函数的基类型。”

0

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

特别地,如果对象的生命周期是通过std::shared_ptr<Base>分配管理,则析构函数将被非虚拟地调用。

std::shared_ptr<Base> sptr = std::make_shared<Derived>(args);

1
我们在工作中遇到了问题,将缺少虚析构函数的类中的 std::shared_ptr 提升为 std::unique_ptr - Jarod42
@Jarod42 我会称之为“降级”。但我理解你的意思。 - SergeyA

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接