关于c ++:“零规则”是否也适用于具有虚拟方法的类?

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
3
4
5
6
7
8
struct Base {
    virtual void drawYourself();
    virtual ~Base() = default;
};

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

但除此之外,零规则仍然有效。

如果您按照您的方式进行,或者如果您不使用virtual析构函数,那么当delete通过基指针生成派生对象时,您只会得到未定义的行为。