Does “The Rule of Zero” also apply for classes with virtual methods?
我发现彼得·索默拉德(PeterSommerlads)幻灯片(第32页)中提到的零规则非常引人注目。
不过,我似乎记得有一个严格的规则,如果类具有虚拟成员并且实际上是派生的,那么必须定义析构函数virtual。
1 2 3 4 5 6 7
| struct Base {
virtual void drawYourself();
virtual ~Base() {}
};
struct Derived : public Base {
virtual void drawYourself();
}; |
析构函数的主体甚至可能是空的(它只需要vtbl中的条目)。
我似乎记得当使用层次结构时
1 2 3 4 5
| int main() {
Base *obj = new Derived{};
obj->drawYourself(); // virtual call to Derived::drawYourself()
delete obj; // Derived::~Derived() _must_ be called
} |
那么,重要的是,delete obj调用正确的析构函数。如果我完全忽略了析构函数定义,它就不会变为虚拟的,因此会调用错误的d'tor,这是正确的吗?
1 2 3 4
| struct Base {
virtual void drawYourself();
// no virtual destructor!
}; |
这就引出了我的最后一个问题:
- 在使用虚拟方法的层次结构中,"零规则"也是真的吗?
- 或者在这些情况下,我需要定义虚拟析构函数吗?
编辑:我在回答中被提醒,我的1sr版本的问题有错误的假设。相关(虚拟)析构函数在Base中,而不是Derived中。但我的问题是:我需要声明(虚拟)析构函数吗?
- 1)当然。2)当您认为要以多态方式访问delete时,需要定义一个虚拟析构函数。
- 该死。我发现"零规则"如此引人注目,因为如果应用得当,到目前为止它也没有例外(与C++中的其他东西不同)。现在这种希望消失了…;-)
- = defaulting特别成员不以任何方式违反Ro0。重要的是,您不需要自己实现它们的功能,而是从处理单一责任的构建块(如unique_ptr和vector中派生出来。而tbh,即使你有它,没有= default,对于一个析构函数也是可以的-因为你不需要手动在其中做任何事情。一切仍由适当的成员析构函数处理。
- 你在基类中有一个bug
- @乙?是什么?谢谢。有了4K+积分,你就有了改正它的执照?嗯,你也有权告诉我具体的情况;-)不,真的,谢谢。我纠正了'base&180中的错误析构函数;
- 值得注意的是,如果使用Derived*或make_shared构建,std::shared_ptr将通过调用Derived::~Derived()进行清理,而不要求Base::~Base()是虚拟的。
- @本沃伊特哦,这很有趣,你的评论是+1。
- 我有权修改你的问题,但看起来你没有复制粘贴它。这意味着你还没有真正尝试过你的例子,否则你会看到的。汇编确实对这类事情提出了抱怨。
- @本沃伊特,我想知道江户十一〔10〕和江户十一〔2〕是怎么做到的。我知道自定义删除程序。是这样吗?是的,一定是。shared_ptr的构造函数使用已实例化的类型设置其自定义删除程序。人力资源管理硕士。对的?
- @汤伊:这对埃多克斯1〔13〕是正确的。在std::unique_ptr中,您不会得到这种行为,因为自定义删除程序是类型的一部分(即,所有std::unique_ptr将以相同的方式删除对象)。因此,unique_ptr自定义删除程序对于多态性实际上并不有用。
它实际上是必须声明为虚的基本析构函数,并且在派生类中自动是虚的:
1 2 3 4 5 6 7 8
| struct Base {
virtual void drawYourself();
virtual ~Base() = default;
};
struct Derived : public Base {
virtual void drawYourself();
}; |
但除此之外,零规则仍然有效。
如果您按照您的方式进行,或者如果您不使用virtual析构函数,那么当delete通过基指针生成派生对象时,您只会得到未定义的行为。
- 愚蠢的我。让我们修正我的问题。
- 您可能希望将"constructor"更改为"destructor"
- @布里安比,谢谢,修好了。
- 不是"除了这个",这仍然属于Ro0和= default的范畴。
- @Xeo是的,也许吧,但你还是要记住它。在前面写上virtual。并且只适用于具有其他虚拟方法的类。如果没有派生类(实际上这是没有意义的),就可以省去它。不管怎样,我们还是要解释原因。