每个人都知道基类的析构函数通常必须是虚拟的.但是衍生类的析构函数是什么?在C++ 11中,我们有关键字"override"和显式使用默认析构函数的能力.
struct Parent
{
std::string a;
virtual ~Parent()
{
}
};
struct Child: public Parent
{
std::string b;
~Child() override = default;
};
Run Code Online (Sandbox Code Playgroud)
在Child类的析构函数中使用关键字"override"和"= default"是否正确?在这种情况下编译器会生成正确的虚析构函数吗?
如果是,那么我们可以认为它是好的编码风格,我们应该总是以这种方式声明派生类的析构函数,以确保基类析构函数是虚拟的吗?
Rei*_*ica 18
在Child类的析构函数中使用关键字"override"和"= default"是否正确?在这种情况下编译器会生成正确的虚析构函数吗?
是的,这是正确的.在任何理智的编译器上,如果代码编译没有错误,这个析构函数定义将是一个无操作:它的缺席不能改变代码的行为.
我们可以认为这是一种很好的编码风格
这是一个偏好问题.对我来说,只有基类类型是模板化的才有意义:它将强制要求基类具有虚拟析构函数.否则,当基类型固定时,我会认为这样的代码是噪声.它不像基类会神奇地改变.但是如果你有一些死去的队友喜欢在没有检查代码的情况下改变它们,而这些代码依赖于它们可能会破坏的东西,那么最好将析构函数定义留在 - 作为额外的保护层.
Ser*_*eyA 12
override
只不过是一个安全网.如果基类析构函数是虚拟的,子类的析构函数将始终是虚拟的,无论它是如何声明的 - 或者根本不声明(即使用隐式声明的析构函数).
小智 8
我认为“覆盖”对析构函数有点误导。当你重写虚函数时,你就替换了它。析构函数是链接的,所以你不能从字面上覆盖析构函数
根据CppCoreGuidelines C.128,不应声明派生类的析构函数virtual
或override
。
如果基类析构函数被声明为 virtual,则应避免声明派生类析构函数
virtual
或override
. 一些代码库和工具可能会坚持覆盖析构函数,但这不是这些指南的建议。
更新:回答为什么我们有一个特殊的析构函数的问题。
方法覆盖是一种语言功能,它允许子类或子类提供已由其超类或父类之一提供的方法的特定实现。子类中的实现通过提供与父类中的方法具有相同名称、相同参数或签名以及相同返回类型的方法来覆盖(替换)超类中的实现。
换句话说,当你调用一个被覆盖的方法时,只有该方法的最后一个实现(在类层次结构中)被实际执行,而所有的析构函数(从最后一个子级到根父级)必须被调用以正确释放所有拥有的资源由对象。
因此,我们并没有真正替换(覆盖)析构函数,而是将额外的一个添加到对象析构函数链中。
更新:此CppCoreGuidelines C.128规则已更改(由1448、1446问题)以简化已经详尽的例外列表。所以一般规则可以概括为:
对于类用户,包括析构函数在内的所有虚函数都是同样多态的。
override
在国有子类上标记析构函数是教科书式的卫生,你们都应该按例行事(参考文献)。
在override
这里使用(至少)一个原因- 确保基类的析构函数始终是虚拟的.如果派生类的析构函数认为它覆盖了某些东西,那将是一个编译错误,但没有什么可以覆盖.如果您这样做,它还为您提供了一个方便的位置来保留生成的文档.
另一方面,我可以想到两个不这样做的理由:
如果在标题中定义了一个destuctor(或者如果它是内联的),那么你确实会引入奇怪的编译错误.假设您的课程如下:
struct derived {
struct impl;
std::unique_ptr<derived::impl> m_impl;
~derived() override = default;
};
Run Code Online (Sandbox Code Playgroud)
您可能会遇到编译器错误,因为析构函数(此处与类内联)将查找不完整类的析构函数derived::impl
.
这是我的一种说法,即每行代码都可以成为一种负担,也许最好只是跳过一些东西,如果它在功能上什么也不做.如果你真的真的需要强制执行从父类的基类虚析构函数,有人建议使用static_assert
在音乐会std::has_virtual_destructor
,这将产生更一致的结果,恕我直言.
归档时间: |
|
查看次数: |
17433 次 |
最近记录: |