虚拟析构函数的默认覆盖

时间:2016-12-06 16:08:03

标签: c++ c++11 c++14

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

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

};

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

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

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

7 个答案:

答案 0 :(得分:15)

  

使用两个关键字"覆盖"是否正确?和" =默认"在Child类的析构函数中?在这种情况下编译器会生成正确的虚析构函数吗?

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

  

我们可以认为这是一种很好的编码风格

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

答案 1 :(得分:11)

SELECT u.user_country, u.user_id, u.user_email, u.user_verified, c.country_iso, c.country_nicename, count(distinct w.website_id) as websitecount FROM user u LEFT JOIN country c ON c.country_id = u.user_country LEFT JOIN websites w ON w.website_owner = u.user_id GROUP BY u.user_country, u.user_id, u.user_email, u.user_verified, c.country_iso, c.country_nicename 只不过是一个安全网。如果基类析构函数是虚拟的,子类的析构函数将始终是虚拟的,无论它是如何声明的 - 或者根本不声明(即使用隐式声明的析构函数)。

答案 2 :(得分:6)

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

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

  • 对于派生类来说,从基类强制执行行为有点奇怪和倒退。
  • 如果在标题中定义了一个destuctor(或者如果它是内联的),那么你确实会引入奇怪的编译错误。让我们说你的班级看起来像这样:

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

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

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

    < / LI>

答案 3 :(得分:2)

我认为“覆盖”会对析构函数产生误导。 覆盖虚拟功能时,将替换它。 析构函数是链接的,所以你不能逐字地覆盖析构函数

答案 4 :(得分:1)

CPP Reference表示override确保函数为virtual,并确实覆盖了虚函数。因此override关键字将确保析构函数是虚拟的。

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

你不需要做任何事情。离开Child dtor undefined可以正常工作:

#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;
}

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

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

答案 5 :(得分:1)

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

  

如果基类析构函数被声明为虚拟的,则应避免声明派生类析构函数virtualoverride。一些代码库和工具可能会坚持要求使用析构函数进行重写,但这不是这些准则的建议。

答案 6 :(得分:0)

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

来自C ++标准(10.3虚拟功能)

  

6即使析构函数不是继承的,派生类中的析构函数也是如此   class 覆盖一个声明为virtual的基类析构函数;见12.4和   12.5。

另一方面,也写了(9.2级成员)

  

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

虽然析构函数被称为特殊成员函数,但它们也是成员函数。

我确信应该对C ++标准进行编辑,以确定析构函数是否具有virt-specifier override是明确的。目前尚不清楚。

相关问题