“零法则”是否也适用于具有虚拟方法的类?

Does "The Rule of Zero" also apply for classes with virtual methods?

提问人:towi 提问时间:2/8/2014 最后编辑:Glorfindeltowi 更新时间:6/16/2022 访问量:1944

问:

我发现 Peter Sommerlads 幻灯片(第 32 页)中也提到的零法则非常引人注目。

虽然,我似乎记得有一个严格的规则,即如果类具有虚拟成员并且实际上是派生的,则必须定义析构函数虚拟

struct Base {
    virtual void drawYourself();
    virtual ~Base() {}
};
struct Derived : public Base {
    virtual void drawYourself();
};

析构函数的主体甚至可能是空的(它只需要 vtbl 中的条目)。

我似乎记得使用层次结构时

int main() {
    Base *obj = new Derived{};
    obj->drawYourself(); // virtual call to Derived::drawYourself()
    delete obj; // Derived::~Derived() _must_ be called
}

然后,调用正确的析构函数很重要。如果我完全省略析构函数定义,它就不会变成虚拟的,因此会调用错误的 d'tor,这是正确的吗?delete obj

struct Base {
    virtual void drawYourself();
    // no virtual destructor!
};

这就引出了我的最后一个问题:

  • “零法则”在具有虚拟方法的层次结构中是否也适用
  • 或者在这些情况下,我是否需要定义虚拟析构函数?

编辑:正如我在回答中被提醒的那样,我的 1sr 版本的问题有错误的假设。相关的(虚拟)析构函数在 中,而不是 。但我的问题是:我是否需要声明(虚拟)析构函数?BaseDerived

C++ C++11 函数 虚拟析构函数 零规则

评论

1赞 Shoe 2/8/2014
1)当然。2)当你认为你要多态时,你需要定义一个虚拟析构函数。delete
0赞 towi 2/8/2014
该死的。我发现“零法则”如此引人注目,因为如果应用得当,到目前为止它没有例外(与C++中的其他所有内容不同)。现在这个希望已经消失了...... ;-)
6赞 Xeo 2/8/2014
= defaulting 特殊成员不会以任何方式违反 Ro0。重要的部分是,您不会自己实现它们的功能,而是从处理单个责任的构建块(如 和 )中派生它。而且 tbh,即使你有它,没有 ,对于析构函数来说也很好 - 因为你不需要手动在其中做任何事情。一切仍由相应成员的析构函数处理。unique_ptrvector= default
6赞 Ben Voigt 2/8/2014
值得注意的是,如果构造自 (或 ) 将通过调用进行清理,并且不需要是虚拟的。std::shared_ptr<Base>Derived*make_shared<Derived>Derived::~Derived()Base::~Base()
3赞 Ben Voigt 2/9/2014
@towi:没错。你不会得到这种行为,因为自定义删除程序是类型的一部分(即所有删除程序都将以相同的方式删除对象)。因此,自定义删除器对于多态性确实没有用处。std::shared_ptrstd::unique_ptrstd::unique_ptr<Base, ThisDeleterType>unique_ptr

答:

8赞 user3175411 2/8/2014 #1

它实际上是必须声明为虚拟的基析构函数,并且在派生类中它会自动是虚拟的:

struct Base {
    virtual void drawYourself();
    virtual ~Base() = default;
};

struct Derived : public Base {
    virtual void drawYourself();
};

但除此之外,零法则仍然成立。

如果你按照你的方式去做,或者如果你省略了析构函数,你只是在通过基指针连接派生对象时得到未定义的行为。virtualdelete

评论

0赞 towi 2/8/2014
傻傻的我。让我们修正一下我的问题。
3赞 Brian Bi 2/8/2014
您可能希望将“constructor”更改为“destructor”
1赞 Xeo 2/8/2014
不是“除此之外”——这仍然属于 Ro0 和 .= default
1赞 towi 2/8/2014
@Xeo是的,也许吧,但人们仍然必须记住它。并在它前面写。并且仅适用于具有其他虚拟方法的类。如果没有派生类,可以饶恕它(实际上,这毫无意义)。无论如何,人们仍然需要解释为什么。virtual