条款07:为多态基类声明虚析构函数

看下面一个类:

class TimeKeeper{

public :

     TimeKeeper();

     ~TimeKeeper();

     ....

};

class AtomicClock : public TimeKeeper{...};

class WaterClock : public TimeKeeper{...};

class WristClock : public TimeKeeper{...};

         当我们使用时,我们可以采用一个工厂方法,用基类的指针指向派生类的对象:

         TimeKeeper * getTimeKeeper();

        

TimeKeeper * ptk = getTimeKeeper(); //用基类的指针指向派生类的对象

                   ....

delete ptk;

 

(注,在以后,我会说明这种依赖用户来delete的方法不是一个好方法。但是现在不是我们所要讨论的问题。我们要讨论的问题是:。。。。)

我们要讨论的是,当我们把ptk进行析构时,会发生什么事情?

 

由于ptk是一个基类的指针,当进行析构时,会调用基类的析构函数,也就是说会把ptk所指对象的基类部分进行,但是ptk实际指向了一个派生类的对象,对于派生类的数据怎么办呢?此时会产生不明确行为,这是造成内存泄露的一个绝佳方式。

 

如果我们把基类的析构函数做成虚函数,则这个问题就可以解决了:

         class TimeKeeper{

public :

     TimeKeeper();

     virtual ~TimeKeeper();

     ....

};

class AtomicClock : public TimeKeeper{...};

class WaterClock : public TimeKeeper{...};

class WristClock : public TimeKeeper{...};

        

         由于基类的析构函数是一个虚函数,则每一个派生类中的析构函数都是虚函数。当我们再次使用一个基类指针指向派生类的时候,如下:

TimeKeeper * ptk = getTimeKeeper();

....

delete ptk;

再次进行析构时,由于析构函数是一个虚函数,所以在析构时,会在运行期来决定调用哪个类的析构函数,此时,经判断是派生类的析构函数,所以会调用派生类的析构函数,此时,可以把ptk所指向的对象全部析构。

 

结论:任何class只要带有virtual函数都几乎确定应该也有一个virtual析构函数。

 

       如果一个class不含virtual函数,通常表示它并不意图被用做一个base class,即使被当作基类,也不希望用基类的指针来指向一个派生类的对象。当class不企图被当作base class,令其析构函数为virtual,往往不是一个好主意。如下类:

class Point{

public :

     Point(int xCoord, int yCoord) : x(xCoord),y(yCoord){}

     ~Point();

private:

     int x, y;

}

这样一个类的对象,会占用64个字节的内存,如果其析构函数是一个虚函数,则此类的对象,将会是96字节,这样,浪费了50%的空间。

所以许多人的心得是:只有当class内含至少一个virtual函数,才为它声明一个virtual析构函数。

 

         请记住:

  • Polymorphic base class 应该声明一个virtual析构函数。如果class带有任何virtual函数,它就应该拥有一个virtual析构函数。
  • Class的设计目的如果不是作为base class 使用,或不是为了具备多态性,就不应该声明virtual析构函数。s
posted @ 2012-11-15 22:29  loveyakamoz  阅读(275)  评论(0编辑  收藏  举报