假定有如下的两个类:
class Base {
// some virtual methods
};
class Derived : public Base {
~Derived() {
// Do some important cleanup
}
};
然后有下面的语句:
Base *b = new Derived();
delete b; // Here's the problem! 行为未定义!!!
解决办法是将 Base 的析构函数定义为 virtual。当删除指针时,如果指针的对象的静态类型不同于它的动态类型,那么:静态类型必须为基类,且基类必须定义 virtual destructor,否则行为就是未定义的。而且,如果没有将基类的析构函数定义为 virtual, 当delete指针时,只有基类的析构函数被调用,派生类的析构函数不会被调用,这会导致资源泄漏,总而言之,如果将基类用于多态,那么,基类的析构函数应该总是定义 virtual destructor。
[Effective C++] 假定有如下表示敌方目标的类,静态变量用于记录对象数目:
class EnemyTarget {
public:
EnemyTarget() { ++numTargets; }
EnemyTarget(const EnemyTarget&) { ++numTargets; }
~EnemyTarget() { --numTargets; }
static size_t numberOfTargets() { return numTargets; }
virtual bool destroy(); // returns success of attempt to destroy EnemyTarget object
private:
static size_t numTargets; // object counter
};
// class statics must be defined outside the class, initialization is to 0 by default
size_t EnemyTarget::numTargets;
定义敌方坦克类如下:
class EnemyTank: public EnemyTarget {
public:
EnemyTank() { ++numTanks; }
EnemyTank(const EnemyTank& rhs) : EnemyTarget(rhs) { ++numTanks; }
~EnemyTank() { --numTanks; }
static size_t numberOfTanks() { return numTanks; }
virtual bool destroy();
private:
static size_t numTanks; // object counter for tanks
};
然后,用 new 创建 EnemyTarget
对象并随后删除指针:
EnemyTarget *targetPtr = new EnemyTank;
...
delete targetPtr;
虽然看起来都ok,但是,程序的行为不确定!对此 C++ language standard 给出了十分明确的说明: 如果你试图通过基类的指针删除派生类对象,而同时基类有非虚拟的析构函数时,结果未定义。这就意味着,编译器可能会生成代码随心所欲地执行一些操作:例如格式化硬盘,发邮件,发传真等等。(经常发生的事情是,派生类的析构函数从未被调用,在这个例子中,targetPtr
被删除,EnemyTank
的数量却并未随之调整) ,为了避免这个问题,你只需要将 EnemyTarget
的析构函数定义为virtual,这将保证程序的正确行为: 在释放内存之前 EnemyTank
和 EnemyTarget
的析构函数都将被调用。
但是,如果一个类并不包含虚拟函数,则表明该类的没有用作基类的意图,此时如果该类的析构函数定义为virtual,会是不好的做法。理由之一是,定义了虚拟函数的类会带有一个额外的指针(virtual table pointer) vptr 指向函数指针数组(virtual table),这个 vptr
占4个字节,会使对象的size增加,会使得对象不能存放于寄存器或者传递给其他语言的函数等(???不确定什么情况下会出现。)
定义virtual destructor的原则总结如下,这个原则适用于绝大多数情况:
当且仅当类定义了至少一个virtual函数时,将析构函数定义为virtual。
如果下列几项有一个成立,就要避免使用虚拟析构函数:
在继承的关系中,用作根类的类一般定义虚拟析构函数。
Effective C++ Item 14: Make sure base classes have virtual destructors.
C++ Primer 15.4.4
When should you not use virtual destructors?
When to use virtual destructors?