是的,您绝对可以将= 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() 时才会收到错误消息。
这至少有两个你应该注意的含义:
- 如果您只想在编译包含类声明的任何内容时遇到错误,您将不会得到它,因为它在技术上是有效的。
- 如果您最初总是将
= default 用于此类析构函数,那么您不必担心超类的析构函数被删除,如果事实证明没问题并且您从未真正使用过它。这是一种异国情调的用途,但在某种程度上它更正确和面向未来。只有当你真正使用析构函数时,你才会得到错误 - 否则,你将独自一人。
因此,如果您要采用防御性编程方法,您可能需要考虑简单地使用{}。否则,您可能最好使用= defaulting,因为这样可以更好地遵守获得正确、有效的代码库所需的最少编程指令,并避免意外后果1。
至于= 0:是的,这仍然是正确的做法。但请注意,它实际上并没有指定“没有默认实现”,而是说 (1) 类不可实例化; (2) 任何派生类都必须覆盖该函数(尽管它们可以使用超类提供的可选实现)。换句话说,您既可以定义一个函数,也可以将其声明为纯虚函数。这是一个例子:
struct A { virtual ~A() = 0; }
A::~A() = default;
这将确保对 A(及其析构函数)的这些约束。
1) 一个很好的例子说明了为什么这会以意想不到的方式有用,有些人总是使用带括号的return,然后 C++14 添加了decltype(auto),这实际上创建了带括号和不带括号的技术区别。