1.基类为什么基类的析构函数是虚函数需要虚析构函数

一.什么时候要用虚析构函数?

通過基类的指针来删除派生类的对象时基类的析构函数应该是虚的。否则其删除效果将无法实现

一般情况下,这样的删除只能够删除基類对象而不能删除子类对象,形成了删除一半形象从而千万内存泄漏。

在公有继承中基类对派生类及其对象的操作,只能影响到那些从基类继承下来的成员如果想要用基类对非继承成员进行操作,则要把基类的这个操作(函数)定义为虚函数

那么,析构函数自然吔应该如此:如果它想析构子类中的重新定义或新的成员及对象当然也应该声明为虚的。

如果不需要基类对派生类及对象进行操作则鈈能定义虚函数(包括虚析构函数),因为这样会增加内存开销

//注意:基类的析构函数被定义为虚的

注:似乎这样的使用经常在newdelete行为时。

我们知道用C++开发的时候,用来做基类的类的析构函数一般都是虚函数可是,为什么基类的析构函数是虚函数要这样做呢下面用一個小例子来说明:   

 这个很简单,非常好理解

 但是,如果把类ClxBase析构函数前的virtual去掉那输出结果就是下面的样子了:

也就是说,类ClxDerived的析构函數根本没有被调用!一般情况下类的析构函数里面都是释放内存资源而析构函数不被调用的话就会造成内存泄漏。我想所有的C++程序员都知道这样的危险性当然,如果在析构函数中做了其他工作的话那你的所有努力也都是白费力气。

所以文章开头的那个问题的答案就昰--这样做是为了当用一个基类的指针删除一个派生类的对象时,派生类的析构函数会被调用

当然,并不是要把所有类的析构函数都寫成虚函数

因为当类里面有虚函数的时候,编译器会给类添加一个虚函数表里面来存放虚函数指针,这样就会增加类的存储空间所鉯,只有当一个类被用来作为基类的时候才把析构函数写成虚函数。

写成虚的是为了在实现多态的时候不造成内存泄露, 比如:

这句delete, 如果你基类的析构函数不是虚的的话, 就会造成内存泄露, 具体表现为派生类的内存被释放了而基类没有.(疑问:似乎应该是基类内存释放而派生类內存没有释放)

 直接的讲C++中基类采用virtual虚析构函數是为了防止内存泄漏。具体地说如果派生类中申请了内存空间,并在其析构函数中对这些内存空间进行释放假设基类中采用的是非虛析构函数,当删除基类指针指向的派生类对象时就不会触发动态绑定因而只会调用基类的析构函数,而不会调用派生类的析构函数那么在这种情况下,派生类中申请的空间就得不到释放从而产生内存泄漏所以,为了防止这种情况的发生C++中基类的析构函数应采用virtual虚析构函数。

现有Base基类其析构函数为非虚析构函数。Derived1和Derived2为Base的派生类这两个派生类中均有以string* 指向存储其name的地址空间,name对象是通过new创建在堆仩的对象因此在析构时,需要显式调用delete删除指针归还内存否则就会造成内存泄漏。

我们看下面对其析构情况进行测试:

d1为Derived1类的指针咜指向一个在堆上创建的Derived1的对象;d2为一个在栈上创建的对象。其中d1所指的对象需要我们显式的用delete调用其析构函数;d2对象在其生命周期结束時系统会自动调用其析构函数。看下其运行结果:


刚才我们说Base基类的析构函数并不是虚析构函数,现在结果显示派生类的析构函数被调用了,正常的释放了其申请的内存资源这两者并不矛盾,因为无论是d1还是d2两者都属于静态绑定,而且其静态类型恰好都是派生类因此,在析构的时候即使基类的析构函数为非虚析构函数,也会调用相应派生类的析构函数

下面我们来看下,当发生动态绑定时吔就是当用基类指针指向派生类,这时候采用delete显式删除指针所指对象时如果Base基类的析构函数没有virtual,会发生什么情况

从上面结果我们看箌,尽管派生类中定义了析构函数来释放其申请的资源但是并没有得到调用。原因是基类指针指向了派生类对象而基类中的析构函数卻是非virtual的,之前讲过虚函数是动态绑定的基础。现在析构函数不是virtual的因此不会发生动态绑定,而是静态绑定指针的静态类型为基类指针,因此在delete时候只会调用基类的析构函数而不会调用派生类的析构函数。这样在派生类中申请的资源就不会得到释放,就会造成内存泄漏这是相当危险的:如果系统中有大量的派生类对象被这样创建和销毁,就会有内存不断的泄漏久而久之,系统就会因为缺少内存而崩溃

        也就是说,在基类的析构函数为非虚析构函数的时候并不一定会造成内存泄漏;当派生类对象的析构函数中有内存需要收回,并且在编程过程中采用了基类指针指向派生类对象如为了实现多态,并且通过基类指针将该对象销毁这时,就会因为基类的析构函數为非虚析构函数而不触发动态绑定从而没有调用派生类的析构函数而导致内存泄漏。

        因此为了防止这种情况下内存泄漏的发生,最恏将基类的析构函数写成virtual虚析构函数

下面把Base基类的析构函数改为虚析构函数:


这样就会实现动态绑定,派生类的析构函数就会得到调用从而避免了内存泄漏。



觉得本文有帮助欢迎点赞并分享给更多的人。

阅读更多更新文章请关注微信公众号【面向对象思考】

我要回帖

更多关于 为什么基类的析构函数是虚函数 的文章

 

随机推荐