每个人都知道基类的析构函数通常必须是虚的。但是派生类的析构函数是什么呢?在C++11中,我们有关键字“override”和显式使用默认析构函数的能力。
struct Parent
{
std::string a;
virtual ~Parent()
{
}
};
struct Child: public Parent
{
std::string b;
~Child() override = default;
};
字符串
在Child类的析构函数中同时使用关键字“override”和“=default”是否正确?在这种情况下,编译器是否会生成正确的虚拟析构函数?
如果是,那么我们是否可以认为这是一种好的编码风格,并且我们应该始终以这种方式声明派生类的析构函数,以确保基类析构函数是虚拟的?
7条答案
按热度按时间xfyts7mz1#
在Child类的析构函数中同时使用关键字“override”和“=default”是否正确?在这种情况下,编译器是否会生成正确的虚拟析构函数?
是的,它是正确的。在任何正常的编译器上,如果代码编译无误,这个析构函数定义将是一个空操作:它的缺失不能改变代码的行为。
我们可以认为它是好的编码风格吗?
这是一个偏好的问题。对我来说,只有当基类类型被模板化时才有意义:那么,它将强制要求基类具有虚析构函数。否则,当基类被修复时,我认为这样的代码是噪音。它并不像基类会神奇地改变。但是如果你有一些愚蠢的队友,他们喜欢在不检查代码的情况下改变东西,而这些代码取决于他们可能破坏的东西,那么最好保留析构函数定义,作为额外的保护层。
vsaztqbk2#
override
只不过是一个安全网。如果基类析构函数是虚的,那么子类的析构函数将始终是虚的,无论它是如何声明的-或者根本没有声明(即使用隐式声明)。1tuwyuhd3#
根据CppCoreGuidelines C.128,派生类的析构函数不应该声明为
virtual
或override
。如果基类的析构函数声明为virtual,则应避免声明派生类的析构函数
virtual
或override
。一些代码库和工具可能坚持重写析构函数,但这不是本指南的建议。UPDATE:为了回答为什么我们有一个特殊的析构函数的问题。
Method overriding是一种语言特性,它允许子类或子类提供一个方法的特定实现,该方法已经由它的超类或父类提供。子类中的实现通过提供与父类中的方法具有相同名称,相同参数或签名以及相同返回类型的方法来覆盖(替换)超类中的实现。
换句话说,当你调用一个被覆盖的方法时,只有该方法的最后一个实现(在类层次结构中)实际上被执行,而所有析构函数(从最后一个子对象到根父对象)必须被调用,以正确地释放对象拥有的所有资源。
因此,我们并没有真正替换(覆盖)析构函数,我们在对象析构函数链中添加了一个额外的析构函数。
更新:CppCoreGuidelines C.128规则被修改(通过1448,1446问题),以简化已经详尽的例外列表。因此,一般规则可以总结为:
对于类用户,所有虚函数包括析构函数都是多态的。
在拥有状态的子类上标记析构函数
override
是教科书上的卫生习惯,你都应该通过例程来做(参考)。hkmswyz64#
在这里使用
override
(至少)有一个原因--确保基类的析构函数总是虚的。如果派生类的析构函数认为它正在重写某些东西,但没有什么可重写的,这将是一个编译错误。如果你这样做,它也给了你一个方便的地方来保留生成的文档。字符串
您可能会得到一个编译器错误,因为析构函数(与这里的类内联)将查找不完整类
derived::impl
的析构函数。这是我迂回的说法,每一行代码都可能成为一种负担,如果它在功能上什么也不做,也许最好跳过一些东西。如果你真的真的需要从父类在基类中强制一个虚析构函数,有人建议使用
static_assert
和std::has_virtual_destructor
,这将产生更一致的结果,恕我直言。jtjikinw5#
我认为“override”在析构函数上有点误导。当你重写虚函数时,你替换了它。析构函数是链接的,所以你不能直接重写析构函数
jum4pzuy6#
CPP Reference表示
override
确保函数是virtual
,并且它确实覆盖了一个虚函数。因此override
关键字将确保析构函数是虚的。如果你指定了
override
而不是= default
,那么你将得到一个链接器错误。你不需要做任何事情。保持
Child
dtor未定义就可以了:字符串
这将输出
dtor
。但是,如果您在Parent::~Parent
处删除virtual
,它将不会输出任何内容,因为这是未定义的行为,正如注解中所指出的那样。如果你不相信基类声明了它是虚的,那么你的
override
和= default
的建议是可行的;我希望有更好的方法来确保这一点,而不是用那些析构函数声明来乱丢代码。km0tfn4u7#
虽然析构函数不是继承的,但标准中明确规定派生类的虚析构函数覆盖基类的析构函数。
C标准(10.3虚函数)
6即使析构函数不是继承的,派生类中的析构函数也会覆盖声明为virtual的基类析构函数;参见12.4和12.5。
另一方面,也有写(9.2类成员)
8 virt-specifier-seq最多只能包含一个virt-specifier。virt-specifier-seq只能出现在虚成员函数的声明中(10.3)。
虽然析构函数像特殊的成员函数一样被调用,但它们也是成员函数。
我确信C标准应该被编辑成这样一种方式,即明确析构函数是否可以有virt-specifier
override
。