目录
1.前言
这里设计一个TimeKeeper base class和一些derived classes作为不同的计时方法,例子
如下:
class TimeKeeper{
public:
TimeKeeper();
~TimeKeeper();
....
};
class AtomicClock:public TimeKeeper{....};//原子钟
class WaterClock:public TimerKeeper{.....};//水钟
class WristWatch:public TimeKeeper{.....};//腕表
在程序编写时,程序员只想在程序中使用时间,而不想操心时间如何被计算等这些细节,这时候我们可以设计factory(工厂)函数(参考设计模式知识),返回指针指向一个计时对象。Factory函数会“返回一个base class指针,指向新生成的derived class对象”。实例如下:
TimeKeeper* getTimeKeeper();//返回一个指针,指向一个TimeKeeper派生类的动态分配对象
为遵守factory函数的规矩,被getTimeKeeper()返回的对象必须位于heap。为了避免内存泄露和其他资源,将factory函数返回的每一个对象进行delete非常重要:
TimeKeeper* ptk=getTimeKeeper();//从TimeKeeper继承体系获得一个动态分配对象
...//运用它
delete ptk;//释放它,避免内存泄露。
上述代码的运行会造成严重的内存泄露,问题出在getTimeKeeper返回的指针指向一个derived class对象(例如AtomicClock),而那个对象却被一个base class指针(例如一个TimeKeeper*指针)被删除,而目前的base class(TimeKeeper)有个non-virtual析构函数。因为这样实际执行时通常导致的后果是derived成分没有被销毁。
比如getTimeKeeper返回的指针指向一个AtomicClock对象,其内的AtomicClock成分(也就是声明于AtomicClock class内的成员变量)很可能没被销毁,而AtomicClock的析构函数也未被执行。然而其base class成分(也就是TimeKeeper这一部分)通常会被销毁,于是造成一个诡异的“局部销毁”对象。
2.Virtual析构的正确用法
解决上面这个问题很简单:即给base class一个virtual析构函数。这样删除derived class对象就会按照正确流程执行,包括derived class成分。
class TimeKeeper{
public:
TimeKeeper();
virtual ~TimeKeeper();
....
};
TimeKeeper* ptk=getTimeKeeper();
....
delete ptk;//现在,不会造成资源泄露
任何一个class只要带有virtual函数都几乎确定应该有一个virtual析构函数。
3.扩展
如果class不含virtual函数,通常表明它并不是被用做一个base class。当class不企图当作base class,令其析构函数为virtual往往是个馊注意。假设有一个表示二维空间点的坐标class:
class point{//一个二维空间点(2D point)
public:
point(int xCoord,int yCoord);
~point();
private:
int x,y;
};
如果int占用32bits,那么point对象可塞入一个64-bit缓冲器中。也可以将point对象当作一个64-bit量;
要实现virtual函数,对象必须携带某些信息,主要用来决定在运行期间决定哪一个virtual函数该被调用。该份携带的信息通常是由一个所谓的vptr(virtual talble pointer)指针指出。vptr指向一个由函数指针构成的数组,成为vtbl(virtual table);每一个带有virtual函数的class都有一个相应的vtbl。当对象调用某一virtual函数,实际被调用的函数取决于该对象的vptr所指向的那个vtbl-编译器在其中寻找适当的函数指针。
vitual函数的实现细节不重要。重要的是假设Point class内含virtual函数,其对象的内存会增加:
在32bit计算机体系结构中将占用64Bits(为了存放两个ints)到96bits(两个ints加上vptr);因此,为point添加一个vptr会增加其对象大小的50%-100%。point对象不再能够塞入一个64-bit缓冲器。
因此,无端地将所有classes的析构函数声明为virtual,是不合理的。