虚析构函数(Virtual Destructor)是一个在基类中声明为vitual的析构函数。虚析构函数用于实现多态的析构行为,解决了通过基类指针或引用删除派生类对象时可能引发的问题。
简单来说,当一个基类指针指向一个派生类对象时,如果基类的析构函数不是虚函数,那么当我们释放该基类指针的时候,就只会调用基类的析构函数,而不会调用派生类的析构函数,从而导致派生类中的资源没有正确地释放,造成资源泄漏
我们用以下这个示例说明问题:
class base{
public:
base(){
cout<<"base start"<<endl;
}
~base(){
cout<<"base delete"<<endl;
}
virtual void vfun()
{
cout<<"base vfun"<<endl;
}
};
class devied:public base
{
public:
devied(){
cout<<"devied start"<<endl;
}
~devied(){
cout<<"devied delete"<<endl;
}
void vfun()
{
cout<<"devied vfun"<<endl;
}
};
void test()
{
//基类指针,指向子类对象
base *b1=new devied();
b1->vfun();
⚡ delete b1;//释放基类指针
}
在以上代码中,我们定义了一个基类base,并在基类中声明了一个虚函数,同时定义了一个devied类,且该类继承了base类,但基类的析构函数并不是virtual的
之后我们在测试函数中定义了一个定义了一个基类指针,指向其子类对象,最后我们通过由于该指针是通过动态分配的,因此我们有责任在使用结束之后释放掉它,现在我们观察delete释放掉这个基类指针之后发生的现象
观察结果发现,我们在最后释放掉这个动态分配的基类指针时,编译器仅仅通过基类析构函数释放掉了基类资源,并没有调用子类析构函数释放子类资源,这样显然会造成资源泄露
现在我们将基类的析构函数声明为virtual,再次观察实验结果
class base{
public:
base(){
cout<<"base start"<<endl;
}
virtual ~base(){
cout<<"base delete"<<endl;
}
virtual void vfun()
{
cout<<"base vfun"<<endl;
}
};
class devied:public base
{
public:
devied(){
cout<<"devied start"<<endl;
}
~devied(){
cout<<"devied delete"<<endl;
}
void vfun()
{
cout<<"devied vfun"<<endl;
}
};
void test()
{
//基类指针,指向子类对象
base *b1=new devied();
b1->vfun();
delete b1;//释放基类指针
}
可以看到,当我们把基类的析构函数声明为virtual时,不仅释放掉了基类资源,同时也成功释放了子类资源
总结,当我们在一个基类中使用了虚函数的时候,为了避免使用多态时资源的泄漏,最好将基类的析构函数也声明为virtual的,从而保证析构函数也可以动态调用以正确释放资源
参考:
《c++ primer》
《effective c++》