如标题所示,如果该类没有父类也没有任何子类,把析构函数还定义成虚的,确实没多大必要吧。对象的构建和析构完全是一个入栈和出栈的过程,也就是说肯定会从父类构造到子类,也肯定会从子类析构到父类,这些都是毋容置疑的。
那么把析构函数定义成virtual有个什么意义了。确实没有多大意义,至少对于一个非delete造成的析构。无论是析构一个堆栈对象还是全局对象,编译器肯定能在编译时就做出决策了。但是,假如有人一时兴起,new了一个子类对象并且将地址存储在基类指针中。那么,你该怎么删除这个对象了,只能delete父类指针了。
问题就出在这里了。你既然delete父类指针,假如是在编译层次决策的话,编译器只能帮你调用父类的析构函数了。但是,事实上你是一个子类对象,那么子类的析构函数没有调用,假如你在子类的析构函数做了些什么释放,结果就是那个释放永远不会执行,这样就造成内存泄漏或者资源重复申请之类的。
其实,上面的这种写法就是利用多态的性质,既然要利用多态,肯定得把析构函数定义成virtual的,那么调用哪个析构函数的决策就能到运行时候再决定。如果基类的析构函数定义成virtual的话,其所有子类的析构函数当然也是virtual的,那么我们删除基类指针的时候,就能通过多态机制决定该调用子类的析构函数,也就是从子类的析构函数开始往基类的析构函数调用释放整个对象。这样就不会造成任何资源泄漏了。
以下的代码展示了这样的一种情况,注意Class CA作为基类,就需要把析构函数定义成virtual的。
#include <stdio.h>
class CA
{
public:
CA(int nS = 100): nSize(nS)
{
nA = new int[nSize];
printf("构造CA\n");
}
virtual ~CA()
{
delete [] nA;
printf("析构CA\n");
}
private:
int* nA;
int nSize;
};
class CB : public CA
{
public:
CB(int nS = 100): nSize(nS)
{
nB = new int[nSize];
printf("构造CB\n");
}
~CB()
{
delete [] nB;
printf("析构CB\n");
}
private:
int* nB;
int nSize;
};
int main()
{
CA* pCA = new CB();
delete pCA;
}
效果如图:
如果删掉CA析构函数前面的virtual,效果如图,
你也可以实验在主函数里面定义些栈变量的CA和CB的对象,无论构造函数是否是virtual的,都能够正确的析构。
http://www.xpc-yx.com/2012/11/10/%e4%b8%ba%e4%bb%80%e4%b9%88%e9%9c%80%e8%a6%81%e5%b0%86%e7%bb%a7%e6%89%bf%e5%b1%82%e6%ac%a1%e4%b8%ad%e7%b1%bb%e7%9a%84%e6%9e%90%e6%9e%84%e5%87%bd%e6%95%b0%e5%ae%9a%e4%b9%89%e6%88%90virtual%e7%9a%84/