我应该默认虚拟析构函数吗?

Should I default virtual destructors?

我有一个摘要 class 声明如下:

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

像这样使用 default 关键字声明析构函数是否被认为是好的做法?有没有更好的方法?

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

是的,您绝对可以对此类析构函数使用 = default。特别是如果你只是想用 {} 替换它。我认为 = default 更好,因为它更明确,所以它立即吸引眼球,毫无疑问。

但是,执行此操作时需要注意以下几点。

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

的时候

编辑: 正如肖恩在评论中敏锐地指出的那样,您还可以在 class 声明之外使用 = default,这样可以兼顾两者世界在这里。


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

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

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

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

这个,实际上,编译,因为编译器看到~B()不能生成,所以它根本不生成它——并声明它如已删除。这意味着只有当您尝试 use/call B::~B().

时才会出现错误

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

  1. 如果您只想在编译包含 class 声明的任何内容时得到错误,您将不会得到它,因为它在技术上是有效的。
  2. 如果你最初总是对这样的析构函数使用= default,那么你就不必担心你的超级class的析构函数被删除了,如果它被证明是好的并且你永远不会真正使用它。这是一种奇特的用法,但在某种程度上它更正确并且 future-proof。如果你真的使用析构函数,你只会得到错误——否则,你将独自一人。

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


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

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

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


1) 一个很好的例子说明了为什么这会以意想不到的方式发挥作用,有些人总是使用带括号的 return,然后是 C++14添加了 decltype(auto),这实质上在使用括号和不使用括号之间产生了技术差异。