[C++] Virtual Destructor

假定有如下的两个类:

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,这将保证程序的正确行为: 在释放内存之前 EnemyTankEnemyTarget 的析构函数都将被调用。

但是,如果一个类并不包含虚拟函数,则表明该类的没有用作基类的意图,此时如果该类的析构函数定义为virtual,会是不好的做法。理由之一是,定义了虚拟函数的类会带有一个额外的指针(virtual table pointer) vptr 指向函数指针数组(virtual table),这个 vptr 占4个字节,会使对象的size增加,会使得对象不能存放于寄存器或者传递给其他语言的函数等(???不确定什么情况下会出现。)

定义virtual destructor的原则总结如下,这个原则适用于绝大多数情况:

当且仅当类定义了至少一个virtual函数时,将析构函数定义为virtual。

如果下列几项有一个成立,就要避免使用虚拟析构函数:

  • No intention to derive classes from it  这个类不打算再派生别的类
  • No instantiation on the heap  不会在堆上实例化
  • No intention to store in a pointer of a superclass  不打算用基类的指针存储
  • no intention to delete an instance via a base class pointer 不打算通过基类的指针删除实例

在继承的关系中,用作根类的类一般定义虚拟析构函数。


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?

你可能感兴趣的:(C++)