下面的示例说明了如何防止复制派生类。它基于一个基类,其中复制构造函数和复制赋值运算符都被声明为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}}?
答案 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)
一般规则是将析构函数设置为公共和虚拟,或者保护和非虚拟。在第一种情况下,您的对象使用可破坏的多态,而虚拟析构函数将做正确的事情。在第二种情况下,它只会作为实际的子类被销毁,并且仍然做正确的事情。