声明:本人学习c++不过月余许,按照c++primer第五版一点一点的啃,以下对虚析构函数的理解源于此书第十五章15.7.1节,或许会有理解错误的地方~,本文只是记录当前自己对虚析构函数的理解。
1.虚析构函数的定义,就是在析构函数前加virtual关键字。
2.何时需要需要析构函数?当一个类希望被子类继承的时候,就应该显示的定义之。
3.为什么希望被子类继承的时候,就应该显示的定义之?
为了理解这个这个问题,需要知道三个方面的知识:
1).delete的执行过程(此处专门指delete 对象指针),它有两个任务,第一,调用调用对象指针所指向的对象的析构函数。第二,释放指针指向的空间,标记其为空闲。
2).众所周知,在继承体系结构中,父类的指针或引用可以绑定子类的对象,那么此时,我们delete一个父类指针时其调用的析构函数应该是子类的析构函数,因为我们申请空间时是通过“new 子类”这种形式的。eg.有两个类base,son,base表基类,son表子类继承自base,我们先忽略其各自定义,看下面的语句
son *s=new son(); base *b=s;//静态类型和动态类型不一致 delete b;//delete时调用的析构函数我们希望应该要是son类的析构函数,但只是我们希望要如此而已。
3).编译器在执行过程是如何进行名字查找的呢?其实编译器是根据对象的静态类型进行查找的。也就是说,delete b时,会去查找base类的析构函数。
那么从上面三点可知,如果我们像以往那样不对基类的析构函数进行特殊处理,那么上例中delete b将会调用base类对象的析构函数,而非son类的析构函数。此时如果我们的son类中有成员指向堆中数据,将会造成内存泄露。
基于此,c++继承机制中引入了虚函数技术。可以让我们运行的函数能进行动态绑定。那么要解决上面的问题,只需要将基类base的析构函数定义成虚函数即可。子类继承父类的时候将会继承父类的虚属性,也就是说不管我们是否在子类中自定义了自己的析构函数,子类的析构函数都是虚函数。那么在执行delete b时,会去动态绑定需要调用的析构函数。
下面还有个两个小例子:
1.先不定义A类析构函数
#include <iostream> using namespace std; class base{ public: base(int *ptr=new int):p(ptr){} virtual ~base(){ if(p)delete p; cout<<"base destructor"<<endl; } private: int *p; }; class A :public base{ public: //~A(){cout<<"A destrucotr"<<endl;} private: base b_obj; }; int main() { A *a=new A(); base *ptrb=a; delete ptrb; system("pause"); }
结果:
第一个base destructor是释放A类的私有成员b_obj造成的。
第二个是调用父类析构函数造成的。
对这个结果理解需要明白析构函数的执行原理和步骤,这里不累赘
2.定义自己的虚析构函数
class A :public base{ public: ~A(){cout<<"A destrucotr"<<endl;}//此处继承了父类的虚属性,无需显示加virtual private: base b; };
结果:
这两个小例子说明了虚析构函数的目的。
注:要理解这些东西需要理解的知识点:
1.析构函数的目的,执行步骤
2.静态类型,动态类型
3.动态绑定
4.编译器如何查找需要调用的函数。