C++ 虚拟析构函数的默认覆盖

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/40999791/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 15:25:11  来源:igfitidea点击:

default override of virtual destructor

c++c++11c++14

提问by Sandro

Everyone knows that the desructor of base class usually has to be virtual. But what is about the destructor of derived class? In C++11 we have keyword "override" and ability to use the default destructor explicitly.

大家都知道基类的析构函数通常必须是虚拟的。但是派生类的析构函数呢?在 C++11 中,我们有关键字“覆盖”和显式使用默认析构函数的能力。

struct Parent
{
  std::string a;
  virtual ~Parent()
  {
  }

};

struct Child: public Parent
{
  std::string b;
  ~Child() override = default;
};

Is it correct to use both keywords "override" and "=default" in the destructor of Child class? Will compiler generate correct virtual destructor in this case?

在 Child 类的析构函数中同时使用关键字“override”和“=default”是否正确?在这种情况下,编译器会生成正确的虚拟析构函数吗?

If yes, then can we think that it is good coding style, and we should always declare destructors of derived classes this way to ensure that base class destructors are virtual?

如果是,那么我们是否可以认为这是一种好的编码风格,并且我们应该始终以这种方式声明派生类的析构函数以确保基类析构函数是虚拟的?

回答by Reinstate Monica

Is it correct to use both keywords "override" and "=default" in the destructor of Child class? Will compiler generate correct virtual destructor in this case?

在 Child 类的析构函数中同时使用关键字“override”和“=default”是否正确?在这种情况下,编译器会生成正确的虚拟析构函数吗?

Yes, it is correct. On any sane compiler, if the code compiles without error, this destructor definition will be a no-op: its absence must not change the behavior of the code.

是的,它是正确的。在任何健全的编译器上,如果代码编译没有错误,这个析构函数定义将是一个空操作:它的缺失不能改变代码的行为。

can we think that it is good coding style

我们可以认为这是好的编码风格吗

It's a matter of preference. To me, it only makes sense if the base class type is templated: it will enforce a requirement on the base class to have a virtual destructor, then. Otherwise, when the base type is fixed, I'd consider such code to be noise. It's not as if the base class will magically change. Butif you have deadheaded teammates that like to change things without checking the code that depends on what they may be possibly breaking, it's best to leave the destructor definition in - as an extra layer of protection.

这是一个偏好问题。对我来说,只有当基类类型被模板化时才有意义:然后它会强制要求基类具有虚拟析构函数。否则,当基本类型固定时,我会认为这样的代码是噪音。基类不会神奇地改变。但是,如果您有头脑不清醒的队友,他们喜欢在不检查取决于他们可能会破坏的内容的代码的情况下进行更改,那么最好将析构函数定义保留在其中 - 作为额外的保护层。

回答by SergeyA

overrideis nothing more than a safety net. Destructor of the child class will always be virtual if base class destructor is virtual, no matter how it is declared - or not declared at all (i.e. using implicitly declared one).

override无非是一张安全网。如果基类析构函数是虚拟的,则子类的析构函数将始终是虚拟的,无论它是如何声明的——或者根本没有声明(即使用隐式声明的)。

回答by cyberbisson

There is (at least) one reason for using overridehere -- you ensure that the base class's destructor is always virtual. It will be a compilation error if the derived class's destructor believes it is overriding something, but there is nothing to override. It also gives you a convenient place to leave generated documentation, if you're doing that.

override这里使用(至少)有一个原因——您确保基类的析构函数始终是虚拟的。如果派生类的析构函数认为它正在覆盖某些东西,但没有什么可以覆盖,这将是一个编译错误。如果您正在这样做,它还为您提供了一个方便的地方来留下生成的文档。

On the other hand,I can think of two reasons not to do this:

另一方面,我可以想到两个不这样做的原因:

  • It's a little weird and backwards for the derived class to enforce behavior from the base class.
  • If you define a destuctor in the header (or if you make it inline), you do introduce the possibility for odd compilation errors. Let's say your class looks like this:

    struct derived {
        struct impl;
        std::unique_ptr<derived::impl> m_impl;
        ~derived() override = default;
    };
    

    You will likely get a compiler error because the destructor (which is inline with the class here) will be looking for the destructor for the incomplete class, derived::impl.

    This is my round-about way of saying that every line of code can become a liability, and perhaps it's best to just skip something if it functionally does nothing. If you really really need to enforce a virtual destructor in the base class from the parent class, someone suggested using static_assertin concert with std::has_virtual_destructor, which will produce more consistent results, IMHO.

  • 派生类从基类强制执行行为有点奇怪和倒退。
  • 如果你在头文件中定义了一个析构函数(或者如果你让它内联),你就会引入奇怪的编译错误的可能性。假设您的课程如下所示:

    struct derived {
        struct impl;
        std::unique_ptr<derived::impl> m_impl;
        ~derived() override = default;
    };
    

    您可能会收到编译器错误,因为析构函数(与此处的类内联)将寻找不完整类的析构函数derived::impl.

    这是我的迂回说法,每一行代码都可能成为负担,也许最好跳过一些功能,如果它在功能上什么都不做。如果您真的需要在父类的基类中强制执行虚拟析构函数,有人建议static_assert与 一起使用std::has_virtual_destructor,这将产生更一致的结果,恕我直言。

回答by Alexandre A.

According to the CppCoreGuidelines C.128the destructor of the derived class should not be declared virtualor override.

根据CppCoreGuidelines C.128,不应声明派生类的析构函数virtualoverride

If a base class destructor is declared virtual, one should avoid declaring derived class destructors virtualor override. Some code base and tools might insist on override for destructors, but that is not the recommendation of these guidelines.

如果基类析构函数被声明为 virtual,则应避免声明派生类析构函数virtualoverride. 一些代码库和工具可能会坚持覆盖析构函数,但这不是这些指南的建议。

UPDATE: To answer the question why we have a special case for destructors.

更新:回答为什么我们有一个特殊的析构函数的问题。

Method overridingis a language feature that allows a subclass or child class to provide a specific implementation of a method that is already provided by one of its superclasses or parent classes. The implementation in the subclass overrides (replaces) the implementation in the superclass by providing a method that has same name, same parameters or signature, and same return type as the method in the parent class.

方法覆盖是一种语言功能,它允许子类或子类提供已由其超类或父类之一提供的方法的特定实现。子类中的实现通过提供与父类中的方法具有相同名称、相同参数或签名以及相同返回类型的方法来覆盖(替换)超类中的实现。

In other words, when you call an overridden method only the last implementation of that method (in the class hierarchy) is actually executed while allthe destructors (from the last child to the root parent) must be called to properly release all the resources owned by the object.

换句话说,当你调用一个被覆盖的方法时,只有该方法的最后一个实现(在类层次结构中)被实际执行,而所有的析构函数(从最后一个子级到根父级)必须被调用以正确释放所有拥有的资源由对象。

Thus we don't really replace (override) the destructor, we add additional one into the chain of object destructors.

因此,我们并没有真正替换(覆盖)析构函数,而是将额外的一个添加到对象析构函数链中。

回答by hakun bahun

I think "override" is kind of misleading on destructor. When you override virtual function, you replace it. The destructors are chained, so you can't override destructor literally

我认为“覆盖”对析构函数有点误导。当您覆盖虚函数时,您将替换它。析构函数是链式的,所以你不能从字面上覆盖析构函数

回答by Martin Ueding

The CPP Referencesays that overridemakes sure that the function is virtualand that it indeed overrides a virtual function. So the overridekeyword would make sure that the destructor is virtual.

CPP参考说,override可确保功能是virtual,它确实覆盖的虚函数。所以override关键字将确保析构函数是虚拟的。

If you specify overridebut not = default, then you will get a linker error.

如果指定override但未指定= default,则会出现链接器错误。

You do not need to do anything. Leaving the Childdtor undefined works just fine:

你不需要做任何事情。让Childdtor 未定义工作正常:

#include <iostream>

struct Notify {
    ~Notify() { std::cout << "dtor" << std::endl; }
};

struct Parent {
    std::string a;
    virtual ~Parent() {}
};

struct Child : public Parent {
    std::string b;
    Notify n;
};

int main(int argc, char **argv) {
    Parent *p = new Child();
    delete p;
}

That will output dtor. If you remove the virtualat Parent::~Parent, though, it will not output anything because that is undefined behavior, as pointed out in the comments.

那将输出dtor. 但是,如果删除virtualat Parent::~Parent,它不会输出任何内容,因为这是未定义的行为,如评论中所指出的。

Good style would be to not mention Child::~Childat all. If you cannot trust that the base class declared it virtual, then your suggestion with overrideand = defaultwill work; I would hope that there are better ways to ensure that instead of littering your code with those destructor declarations.

好的风格根本不提Child::~Child。如果您不能相信基类将其声明为虚拟,那么您的建议与override= default将起作用;我希望有更好的方法来确保不要用那些析构函数声明乱扔代码。

回答by Vlad from Moscow

Though destructors are not inherited there is clear written in the Standard that virtual destructors of derived classes override destructors of base classes.

虽然析构函数不是继承的,但标准中明确写到派生类的虚拟析构函数会覆盖基类的析构函数。

From the C++ Standard (10.3 Virtual functions)

来自 C++ 标准(10.3 虚函数)

6 Even though destructors are not inherited, a destructor in a derived class overridesa base class destructor declared virtual; see 12.4 and 12.5.

6 即使析构函数没有被继承,派生类中的析构函数会覆盖声明为 virtual 的基类析构函数;见 12.4 和 12.5。

On the other hand there is also written (9.2 Class member)

另一方面也有写(9.2班级成员)

8 A virt-speci?er-seq shall contain at most one of each virt-speci?er. A virt-speci?er-seq shall appear only in the declaration of a virtual member function(10.3).

8 virt-speci?er-seq 最多包含每个 virt-speci?er 中的一个。virt-speci?er-seq 应仅出现在虚成员函数(10.3)的声明中。

Though destructors are called like special member functions nevertheless they are also member functions.

尽管析构函数像特殊成员函数一样被调用,但它们也是成员函数。

I am sure the C++ Standard should be edited such a way that it was unambiguous whether a destructor may have virt-specifier override. At present it is not clear.

我确信 C++ 标准应该以这样一种方式进行编辑,即析构函数是否可以具有 virt-specifier 是明确的override。目前还不清楚。