【问题标题】:Should I default virtual destructors?我应该默认虚拟析构函数吗?
【发布时间】:2016-03-26 19:25:39
【问题描述】:

我有一个抽象类,声明如下:

class my_type {
public:
    virtual ~my_type() = default;
    virtual void do_something() = 0;
};

使用default 关键字声明这样的析构函数是否被认为是一种好习惯?有没有更好的办法?

另外,= 0 是一种不指定默认实现的现代 (C++11) 方式,还是有更好的方式?

【问题讨论】:

  • virtual ~foo(){} 有什么问题?
  • 没什么我只是认为C++11的方式是使用default关键字..不是吗?
  • @Humam 请注意,您放错了virtualdata 返回类型。
  • @n.m. = default{} 的行为略有不同,所以这并不是一件容易的事。
  • @M.M.即使它们是虚拟的?

标签: c++ c++11 destructor virtual-functions


【解决方案1】:

是的,您绝对可以将= default 用于此类析构函数。尤其是如果您打算将其替换为 {}。我认为= default 更好,因为它更明确,所以它立即引起了人们的注意并且毫无疑问。

但是,在这样做时需要考虑以下几点。

当你 = default 头文件中的析构函数 (见编辑)(或任何其他特殊功能)时,它基本上是在头文件中定义它.在设计共享库时,您可能希望仅由库而不是在头文件中提供析构函数,以便将来可以更轻松地更改它,而无需重建依赖的二进制文件。但同样,这是因为问题只是= default还是{}


编辑:正如 Sean 在 cmets 中敏锐地指出的那样,您还可以在类声明之外使用 = default,这样可以两全其美。


另一个关键的技术差异是,标准规定无法生成的显式默认函数根本不会生成。考虑以下示例:

struct A { ~A() = delete; };
struct B : A { ~B() {}; }

这将编译,因为您正在强制编译器为 B 的析构函数生成指定的代码(及其隐含的必要条件,例如调用 A 的析构函数)——它不能,因为A的析构函数被删除了。但是,请考虑一下:

struct A { ~A() = delete; };
struct B : A { ~B() = default; }

事实上,这编译,因为编译器看到无法生成~B(),所以它根本不生成它——并将其声明为已删除。这意味着您只有在尝试实际使用/调用B::~B() 时才会收到错误消息。

这至少有两个你应该注意的含义:

  1. 如果您只想在编译包含类声明的任何内容时遇到错误,您将不会得到它,因为它在技术上是有效的。
  2. 如果您最初总是将= default 用于此类析构函数,那么您不必担心超类的析构函数被删除,如果事实证明没问题并且您从未真正使用过它。这是一种异国情调的用途,但在某种程度上它更正确和面向未来。只有当你真正使用析构函数时,你才会得到错误 - 否则,你将独自一人。

因此,如果您要采用防御性编程方法,您可能需要考虑简单地使用{}。否则,您可能最好使用= defaulting,因为这样可以更好地遵守获得正确、有效的代码库所需的最少编程指令,并避免意外后果1


至于= 0:是的,这仍然是正确的做法。但请注意,它实际上并没有指定“没有默认实现”,而是说 (1) 类不可实例化; (2) 任何派生类都必须覆盖该函数(尽管它们可以使用超类提供的可选实现)。换句话说,您既可以定义一个函数,也可以将其声明为纯虚函数。这是一个例子:

struct A { virtual ~A() = 0; }
A::~A() = default;

这将确保对 A(及其析构函数)的这些约束。


1) 一个很好的例子说明了为什么这会以意想不到的方式有用,有些人总是使用带括号的return,然后 C++14 添加了decltype(auto),这实际上创建了带括号和不带括号的技术区别。

【讨论】:

  • 我通常 default 整个“big 5”,根据 5/0 规则。仅供参考:如果您出于某种原因想要使析构函数成为纯虚拟的,您仍然需要为它提供一个实现。
  • “我不会=默认析构函数(或任何其他特殊函数)的情况是,当我想确保它的定义不进入头文件时” - - 您也可以将=default 放在初始类定义之外的函数定义上。我一直这样做,特别是在我不关心微不足道的可破坏性而只想减少链接器压力的情况下。
  • 在您的示例“但是考虑一下:”中,B 的析构函数将被定义为已删除,这与“不生成它”不同。 .
  • 如果我声明(默认)析构函数,是否也声明(默认)移动 ctor/assignment 是一种好习惯?复制 ctor/assignment 怎么样?
  • @jimifiki 用户定义的默认析构函数会删除隐式移动构造函数,因此如果要移动类,则应显式创建它。复制构造函数不受影响。
猜你喜欢
  • 2010-11-10
  • 2014-01-30
  • 2010-10-24
  • 2017-04-21
  • 1970-01-01
  • 2012-07-30
  • 2012-07-14
  • 2011-03-31
  • 2010-09-26
相关资源
最近更新 更多