虚拟析构函数是继承的吗?

发布于 2024-08-20 22:57:52 字数 359 浏览 8 评论 0原文

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

class base {
public:
    virtual ~base () {}
};

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

具体问题:

  1. 1)和2)相同吗? 2)是因为它的基础而自动虚拟还是它“停止”虚拟性?
  2. 派生析构函数如果无关的话可以省略吗?
  3. 声明派生析构函数的最佳实践是什么?声明它是虚拟的、非虚拟的还是如果可能的话省略它?

If I have a base class with a virtual destructor. Has a derived class to declare a virtual destructor too?

class base {
public:
    virtual ~base () {}
};

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

Concrete questions:

  1. Is 1) and 2) the same? Is 2) automatically virtual because of its base or does it "stop" the virtualness?
  2. Can the derived destructor be omitted if it has nothing to do?
  3. What's the best practice for declaring the derived destructor? Declare it virtual, non-virtual or omit it if possible?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

无声静候 2024-08-27 22:57:52
  1. 是的,它们是一样的。派生类没有声明虚拟的东西并不能阻止它成为虚拟的。事实上,如果任何方法(包括析构函数)在基类中是虚拟的,则无法阻止它在派生类中成为虚拟的。在 >=C++11 中,您可以使用 final 来防止它在派生类中被重写,但这并不能阻止它成为虚拟的。
  2. 是的,派生类中的析构函数如果无关的话可以省略。是否虚拟并不重要。
  3. 如果可能的话我会省略它。为了清楚起见,我总是对派生类中的虚拟函数使用virtual关键字或override。人们不应该必须沿着继承层次结构一路向上才能弄清楚函数是虚拟的。此外,如果您的类是可复制或可移动的,而无需声明您自己的副本或移动构造函数,则声明任何类型的析构函数(即使您将其定义为默认)将强制您声明副本和如果您需要,请移动构造函数和赋值运算符,因为编译器将不再为您添加它们。

作为第 3 项的一个小要点。注释中指出,如果未声明析构函数,编译器会生成默认析构函数(仍然是虚拟的)。默认函数是内联函数。

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

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

  1. Yes, they are the same. The derived class not declaring something virtual does not stop it from being virtual. There is, in fact, no way to stop any method (destructor included) from being virtual in a derived class if it was virtual in a base class. In >=C++11 you can use final to prevent it from being overridden in derived classes, but that doesn't prevent it from being virtual.
  2. Yes, a destructor in a derived class can be omitted if it has nothing to do. And it doesn't matter whether or not its virtual.
  3. I would omit it if possible. And I always use either the virtual keyword or override for virtual functions in derived classes for reasons of clarity. People shouldn't have to go all the way up the inheritance hierarchy to figure out that a function is virtual. Additionally, if your class is copyable or movable without having to declare your own copy or move constructors, declaring a destructor of any kind (even if you define it as default) will force you to declare the copy and move constructors and assignment operators if you want them as the compiler will no longer put them in for you.

As a small point for item 3. It has been pointed out in comments that if a destructor is undeclared the compiler generates a default one (that is still virtual). And that default one is an inline function.

Inline functions potentially expose more of your program to changes in other parts of your program and make binary compatibility for shared libraries tricky. Also, the increased coupling can result in a lot of recompilation in the face of certain kinds of changes. For example, if you decide you really do want an implementation for your virtual destructor then every piece of code that called it will need to be recompiled. Whereas if you had declared it in the class body and then defined it empty in a .cpp file you would be fine changing it without recompiling.

My personal choice would still be to omit it when possible. In my opinion it clutters up the code, and the compiler can sometimes do slightly more efficient things with a default implementation over an empty one. But there are constraints you may be under that make that a poor choice.

笑忘罢 2024-08-27 22:57:52

虚拟成员函数将隐式地使该函数的任何重载成为虚拟的。

因此 1) 中的 virtual 是“可选的”,基类析构函数是 virtual 使得所有子析构函数也都是 virtual 的。

A virtual member function will make implicitely any overloading of this function virtual.

So the virtual in 1) is "optional", the base class destructor being virtual makes all child destructors virtual too.

哽咽笑 2024-08-27 22:57:52
  1. 与所有方法一样,析构函数自动是虚拟的。你无法阻止一个方法在 C++ 中成为虚拟的(如果它已经被声明为虚拟的,也就是说,Java 中没有“final”的等价物)
  2. 是的,它可以被省略。
  3. 如果我打算对这个类进行子类化,我会声明一个虚拟析构函数,无论它是否是另一个类的子类化,我也更喜欢继续将方法声明为虚拟的,即使它不是必需的。如果您决定删除继承,这将使子类保持正常工作。但我认为这只是风格问题。
  1. The destructor is automatically virtual, as with all methods. You can't stop a method from being virtual in C++ (if it has already been declared virtual, that is, i.e. there's no equivalent of 'final' in Java)
  2. Yes it can be omitted.
  3. I would declare a virtual destructor if I intend for this class to be subclassed, no matter if it's subclassing another class or not, I also prefer to keep declaring methods virtual, even though it's not needed. This will keep subclasses working, should you ever decide to remove the inheritance. But I suppose this is just a matter of style.
回眸一笑 2024-08-27 22:57:52

1/ 是的
2/ 是的,它将由编译器生成
3/ 选择是否将其声明为虚拟成员应遵循您的重写虚拟成员约定 - 恕我直言,两种方式都有很好的论据,只需选择一种并遵循即可。

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

1/ Yes
2/ Yes, it will be generated by the compiler
3/ The choice between declaring it virtual or not should follow your convention for overriden virtual members -- IMHO, there are good arguments both way, just choose one and follow it.

I'd omit it if possible, but there is one thing which may incite you to declare it: if you use the compiler generated one, it is implicitly inline. There are time when you want to avoid inline members (dynamic libraries for instance).

偷得浮生 2024-08-27 22:57:52

虚函数被隐式覆盖。当子类的方法与基类的虚函数的方法签名匹配时,它就会被覆盖。
这很容易混淆,并且可能在重构过程中被破坏,因此从 C++11 开始就有了 override 和 final 关键字来明确标记这种行为。有相应的警告禁止静默行为,例如 GCC 中的 -Wsuggest-override

SO 上有一个关于 overridefinal 关键字的相关问题:“override”关键字只是检查重写的虚拟方法吗?

以及 cpp 参考中的文档 https://en.cppreference.com/w/cpp /language/override

是否在析构函数中使用 override 关键字仍然存在争议。例如,请参阅此相关SO问题中的讨论:虚拟析构函数的默认覆盖
问题是,虚拟析构函数的语义与普通函数不同。析构函数是链接的,因此所有基类析构函数都在子类之一之后调用。但是,在常规方法的情况下,默认情况下不会调用被重写方法的基本实现。需要时可以手动调用它们。

Virtual functions are overridden implicitly. When the method of a child class matches the method signature of the virtual function from a base class, it is overridden.
This is easy to confuse and possibly break during refactoring, so there are override and final keywords since C++11 to mark this behavior explicitly. There is a corresponding warnings that forbid the silent behavior, for example -Wsuggest-override in GCC.

There is a related question for override and final keywords on SO: Is the 'override' keyword just a check for a overridden virtual method?.

And the documentation in the cpp reference https://en.cppreference.com/w/cpp/language/override

Whether to use override keyword with the destructors is still a bit of debate. For example see discussion in this related SO question: default override of virtual destructor
The issue is, that the semantics of the virtual destructor is different to normal functions. Destructors are chained, so all base classes destructors are called after child one. However, in case of a regular method base implementations of the overridden method are not called by default. They can be called manually when needed.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文