何时以及为什么基类中的析构函数不能被定义为虚拟?

时间:2011-08-10 01:56:38

标签: c++ virtual-destructor

下面的示例说明了如何防止复制派生类。它基于一个基类,其中复制构造函数和复制赋值运算符都被声明为private

class Uncopyable
{
protected:
   // allow construction and destruction of derived objects...
   Uncopyable() {}
   ~Uncopyable() {}

private:
   // but prevent copying...
   Uncopyable(const Uncopyable&);
   Uncopyable& operator=(const Uncopyable&);
};

我们可以使用这个类,结合私有继承,使类不可复制:

class derived: private Uncopyable
{...};

请注意,类Uncopyable中的析构函数未声明为virtual.

以前,我了解到了

  • 基类中的析构函数应为virtual
  • 非基类中的析构函数不应为virtual

在此示例中,Uncopyable的析构函数不是virtual,但它是继承自的。virtual。这似乎违背了我以前学到的智慧。

何时以及为什么基类中的析构函数不应定义为{{1}}?

5 个答案:

答案 0 :(得分:12)

如果您可能尝试通过基类型的指针释放派生类型的对象,则基类析构函数只需要virtual。因此,如果您只从基类私有而不是公开继承,如Uncopyable中的情况,那么您不必担心放入virtual析构函数,因为在使用私有继承时,您无法获取指向派生对象的指针并将其存储在指向基类型的指针中。

另一个例子可能是你要使用像这样的mixin类,它使一个类跟踪对象分配的数量,其中mixin继承来获取行为但不是多态地处理:

template <typename T> class Counter {
public:
    Counter() { ++numInstances; }
    Counter(const Counter&) { ++numInstances );
    ~Counter() { --numInstances; }

    static unsigned getNumInstances() { return numInstances; }

private:
    static unsigned numInstances;
}
template <typename T> unsigned Counter<T>::numInstances = 0;

更一般地说,在使用静态多态时,您不需要虚拟析构函数,因为您从不使用指向基类型的指针来多态地处理类。您只使用指向派生类型的指针。

这里可能还有其他几个案例,但是这两个案例(私有继承,mixin类和静态多态)覆盖了不需要虚拟析构函数的大部分空间。

答案 1 :(得分:2)

当你设计基础不是界面,而是设计为实施细节时(注意private的{​​{1}}继承)。

答案 2 :(得分:1)

如果您知道没有人会将其作为Uncopyable *删除,那么从技术上讲,您不必将您的decostructor设为虚拟,但始终会将其删除为相同的子类。

是的,这基本上是@templatetypedef所说的,但我会以更简单的方式解释它。

所以:如果人们可能会这样做:

void aFunction(Uncopyable* obj) {
    delete obj;
}

然后你应该声明你的析构函数是虚拟的(以确保潜在的子类得到他们的析构函数。

但是,如果您知道有人会删除这样的子类:

class Widget : public Uncopyable
{
  ....
};

void aFunction(Widget* obj) {
   delete obj;
}

然后你不必将你的析构函数设为虚拟(因为将调用子类析构函数)。

至少,这是我的理解。

答案 3 :(得分:0)

当你需要你的对象是普通的旧数据时,没有vtable。如果我需要的话,我会对它进行评论,因为99%的时间在基类析构函数中脱离“虚拟”只是一个有人想要纠正的错误。

答案 4 :(得分:0)

一般规则是将析构函数设置为公共和虚拟,或者保护和非虚拟。在第一种情况下,您的对象使用可破坏的多态,而虚拟析构函数将做正确的事情。在第二种情况下,它只会作为实际的子类被销毁,并且仍然做正确的事情。