假设,有一个父类:
class TimeKeeper{ public: TimeKeeper(); ~TimeKeeper(); ... };
然后假设有很多子类:
class AutomicClock : public TimeKeeper{...};
class WaterClock : public TimeKeeper{...};
class WristWatch : public TimeKeeper {...};
然后再假设有一个 factory 函数:
TimeKeeper* getTimeKeeper(); //返回一个指针,指向一个TimeKeeper // 派生类的动态分配对象。
为了在使用后销毁它:
TimeKeeper* ptk = getTimeKeeper(); //从TimeKeeper继承体系 //获得一个动态分配对象 ... //运用它 delete ptk; //释放它,避免资源泄露。
但是以上方式存在一个根本问题,问题出在getTimeKeeper返回的指针指向一个derived class对象(例如一个AutomicClock),而那个对象却经由一个base class指针(TimeKeeper*)被删除。而目前base class是一个non-virtual函数。在C++中,derived class对象经由一个base class指针被删除,而该base class 带有一个non-virtual析构函数,其结果未有定义。(也就是,derived class成分可能未被销毁:AutomicClock的析构函数也未能被执行。)但是base class会被销毁。这会导致资源泄露,和想自杀。
但是你要是像下面这样搞,那就美了:
class TimeKeeper{
public:
TimeKeeper();
virtual ~TimeKeeper(); //这里是关键
.... };
TimeKeeper* ptk = getTimeKeeper();
...
delete ptk;
任何class只要带virtual函数都几乎确定应该也有一个virtual 析构函数。
如果class不带有virtual函数,说明它不像做base class。当class不企图做一个base class函数的时候,令其析构函数为virtual 往往是个馊主意:
想要实现virtual函数,对象必须携带某些信息,主要用来在运行期决定哪个virtual函数该被调用。这份信息通常是一个所谓vpt(virtual table pointer)指针指出。vptr指向一个 由函数指针构成的数组,称为vtbl(virtual table)。每一个带有virtual的class都有一个相应的vtbl,实际被调用的函数却决于该对象的vptr所指的那个vtbl----编译器在其中寻找适当的函数指针。
所以含有virtual函数会增加class的体积,所以C++的class对象也就不再和其他语言(如C)声明一样有一样的结构。因此也就不再可能把它传递到其他语言所写的函数,不再具有移植性。
当继承non-virtual析构函数class,也是会有问题的。
class SpecialString : public std::string //non-virtual 析构函数 { ... };
当程序中无意将一个 pointer-to-spectialString转换为一个pointer-to-string。然后将转换所得的那个string指针delete掉,就会出问题:
SpectialString* pss = new SpecialString("Impending Doom"); std::string* ps; ... ps = pss; //specialString* => std::string* ... delete ps; //specialString 析构函数没被调用
当希望把class做成一个抽象类,不能被构造对象,所以需要提供一个纯虚函数。但是手上也不知道这里要搞个什么纯虚函数的时候。定义析构函数为纯虚函数。
class AWOV { public: virtual ~AWOV() = 0; }; AWOV : ~AWOV() { } //pure virtual 定义
析构函数的运作顺序是,最深层派生那个析构函数最先被调用,然后是每一个base class 的析构函数被调用。编译器会在AWOV的derived classes的析构函数中创建一个对~AWOV的调用动作,所以你必须为这个函数提供共一份定义,不然连接器会不高兴。
多态性质的base classes应该声明一个virtual 析构函数。如果class带有任何virtual函数,它就应该有一个virtual 析构函数。
Classes的设计目的如果不是作为base classes使用,或不是为了具备多态性质,就不该声明virtual 函数。