条款 07 :为多态基类声明virtual析构函数
Declare destructors virtual in polymorphic base classes.
- 我们有许多计时方法,假设有一个TimeKeeper基类和一些派生类用来表示时间。
class TimeKeeper{
public:
TimeKeeper();
~TimeKeeper();
....
};
class AtomicClock:public TimeKeeper{...};//原子钟
class WaterClock:public TimeKeeper{...};//水钟
class WristClock:public TimeKeeper{...};//腕表
//假设客户只需要使用时间而不关心其实现细节,
//我们用如下方法返回一个多态指针
//此处假设使用工厂设计模式
TimeKeeper* ptk=getTimeKeeper();//返回一个指针,指向派生类的动态分配对象
//由于返回对象位于堆空间,用后必须要清理该空间
delete ptk;//不能成功删除,会发生意外错误
看上述代码发现一个问题:我们指针指向了一个派生类对象,但是当要删除他时却要经由一个基类指针,但是却使用的是非虚析构函数。这明显会出现错误,因为C++明确指出:该通过没有虚析构函数的基类指针删除一个派生类对象,其结果是未定义的———实际上通常会发生派生类继承自基类部分成功删除而新增部分不能成功删除。
解决上述问题的方法是将基类的析构函数声明为虚析构函数,这样就能正确销毁整个对象。
class TimeKeeper{
public:
TimeKeeper();
virtual ~TimeKeeper();
....
};
TimeKeeper* ptk=getTimeKeeper();
delete ptk;//此时能成功销毁
- 一般来说一个类只要带有虚函数,那么他应该也具有一个虚析构函数。
- 一般来说一个类不含有虚函数,通常他不会 用来表示一个基类。在不意图作为基类的类中设计虚析构函数也不是一个高明的做法。因为虚函数带来了额外开销,例如虚函数指针,虚函数表等等。
- 何时声明为虚析构函数合适?一个好的习惯是:只有当class内至少有一个virtual函数,才为他声明virtual析构函数。
- 不要错误的将不带虚析构函数的类作为基类继承。(C++没有像Java,C#等提供final classes 或sealed classes来禁止派生的机制。)
- 纯虚函数(pure virtual function)可以导致abstract(抽象) classes -------也就是类不能被 实例化。
- 要是你想拥有一个抽象类,但是手上又没有任何纯虚函数该怎么做?由于抽象类总是企图作为基类来使用,而基类又应该具有一个虚析构函数,纯虚函数又会导致抽象类。所以解法很简单:为你希望他成为抽象类的那个class声明一个纯虚析构函数。
class AWOV{//一个抽象类
public:
virtual ~AMOV()=0;//声明纯虚析构函数
};
- 给基类一个虚析构函数,这个设计规则只适用于polymorphic(带多态性质)的基类身上。 并不是所有基类的设计目的都是为了实现多态,所以那些类并不需要虚析构函数,例如string和STL容器。
请记住
1. polymorphic(带多态性质的)base classes应该声明一个virtual析构函数。如果class带有任何virtual函数,他就应该拥有一个virtual析构函数。
2. Classes的设计目的如果不是作为base classes使用,或不是为了具备多态性(polymorphically),就不应该声明virtual析构函数。