我们都知道实现多态的一个方法就是使用虚函数。比方说我们用不同的方法记录时间:
class TimerKeeper
{
public:
TimeKeeper();
~TimeKeeper();
...
};
class AtomicClock: public TimerKeeper {...}; //原子钟
class WaterClock: public TimerKeeper {...}; //水钟
class WristClock: public TimerKeeper {...}; //腕钟
若是程序员向在程序中使用时间,我们就可以先设计一个factory函数,返回一个计时对象,factory函数会返回一个base class指针,指向新生成的derived class对象:
TimerKeeper* getTimeKeeper(); //返回一个指针,指向一个TimeKeeper派生类的动态分配对象
TimerKeeper* ptk = getTimeKeeper(); //仓TimeKeeper继承体系获得一个动态分配对象
... //运用该对象
delete ptk; //释放对象
改代码乍一看没什么错误,既定义了一个指针,让他指向派生类,实现自己想要的效果,最后有释放内存。但问题恰恰出现在了释放内存上。getTimerKeeper返回派生类的对象,但对象却时通过基类指针释放,但我们的基类指针没法实现根据返回的是自己的那个基类而进行对应的释放。而实际上其派生类对象的析构函数并未被调用,很可能没有被销毁。于是就产生了一个诡异的局部销毁,造成资源泄露,败坏数据结构,在调试器上浪费时间等。
那么怎么解决呢?让他产生多态就可以了:给基类的析构函数加上virtual关键字。然后会产生动态联编,释放的对象就是根据你的返回对象进行调用相应的析构函数。
声明一个virtual函数一定是我已经确定该函数一定是作为一个基类的,如果不作为基类,最好还是不要声明virtual函数。每个virtual函数都是放在一个虚函数表中的,然后生成一个虚函数指针指向这个虚函数表,当调用的时候就挨个向下寻找所对应的的虚函数。因此,你每声明一个虚函数,类的大小都会多出一个vptr(虚函数指针)的大小,多了50%~100%的空间。另外其他语言可能并没有vptr指针,因此程序也就失去了可移植性。因此,若不是明确作为基类,还是不要声明为虚函数了。