知识背景

要弄明白这个问题,首先要了解下C++中的动态绑定。

关于动态绑定的讲解,请参阅:  C++中的动态类型与动态绑定、虚函数、多态实现

正题

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

示例代码讲解

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

class Base {public:
~Base() {cout << "~Base()" << endl;
}
};

class Derived1 : public Base {public:Derived1():name_(new string("NULL")) {}Derived1(const string& n):name_(new string(n)) {}~Derived1() {delete name_;cout << "~Derived1(): name_ has been deleted." << endl;}private:string* name_;
};class Derived2 : public Base {public:Derived2():name_(new string("NULL")) {}Derived2(const string& n):name_(new string(n)) {}~Derived2() {delete name_;cout << "~Derived2(): name_ has been deleted." << endl;}private:string* name_;
};

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

int main() {Derived1* d1 = new Derived1();Derived2 d2 = Derived2("Bob");delete d1;return 0;
}

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

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

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

int main() {Base* base[2] = {new Derived1(),new Derived2("Bob")      };for (int i = 0; i != 2; ++i) {delete base[i];    }return 0;
}


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

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

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

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

class Base {public:
virtual ~Base() {cout << "~Base()" << endl;
}
};

再看下其运行结果:


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

故: 继承时,要养成的一个好习惯就是,基类析构函数中,加上virtual。

转自:http://blog.csdn.net/iicy266/article/details/11906457

转载于:https://www.cnblogs.com/willhua/p/6785158.html

C++中基类的析构函数为什么要用virtual虚析构函数相关推荐

  1. 在保护继承中基类的共有成员_C++学习大纲:继承方式的调整

    C++ 继承方式的调整 在任何继承方式中,除了基类的private成员外,都可以在派生类中分别调整其访问控制. 调整格式 [public: | protected: | private: ] :: ; ...

  2. C++ 基类和派生类的virtual虚析构函数

    virtual虚函数与C++的多态息息相关,C++中基类采用virtual虚析构函数主要目的是为了防止潜在的内存泄漏. 具体地说,如果派生类中申请了内存空间,并在其析构函数中对这些内存空间进行释放.假 ...

  3. C++中基类与派生类的构造函数和析构函数

    1.Cpp中的基类与派生类的构造函数 基类的成员函数可以被继承,可以通过派生类的对象访问,但这仅仅指的是普通的成员函数,类的构造函数不能被继承.构造函数不能被继承是有道理的,因为即使继承了,它的名字和 ...

  4. 在保护继承中基类的共有成员_C++面向对象:C++ 继承

    面向对象程序设计中最重要的一个概念是继承.继承允许我们依据另一个类来定义一个类,这使得创建和维护一个应用程序变得更容易.这样做,也达到了重用代码功能和提高执行效率的效果. 当创建一个类时,您不需要重新 ...

  5. 在保护继承中基类的共有成员_c++中的继承

    类的继承,是新的类从已有类那里得到已有的特性.从另一个角度来看这个问题,从已有类产生新类的过程就是类的派生. 派生类的定义 class 派生类名:继承方式 基类名1,继承方式 基类名2 {派生类成员声 ...

  6. Qt中基类widget的各个事件函数中包含了ignore()的调用

    void My_Label:: mousePressEvent(QMouseEvent* e){qDebug()<<"子控件按下了";// e->ignore() ...

  7. 在保护继承中基类的共有成员_C#初学者教程系列11:继承

    本文是C#初学者简单教程,这是第11篇.感谢观看,记得关注我,后续还有更多教程文章,谢谢. 本文环境为Visual Studio 2019. 一.什么是继承 继承是面向对象编程的一种基本特性. 借助继 ...

  8. c++中基类与派生类中隐含的this指针的分析

    先不要看结果,看一下你是否真正了解了this指针? 1 #include<iostream> 2 using namespace std; 3 4 class Parent{ 5 publ ...

  9. 在保护继承中基类的共有成员_C++学习刷题13--继承的实现、继承的方式

    一.前言 本部分为C++语言刷题系列中的第13节,主要讲解这几个知识点:继承的实现.继承的方式.欢迎大家提出意见.指出错误或提供更好的题目! 二.知识点讲解 知识点1:继承的实现,可以理解派生类拥有成 ...

最新文章

  1. python实验过程心得体会_Python中django学习心得
  2. 从0到100——知乎架构变迁史
  3. 项目总结1:微信扫码自动识别设备类型并跳转到相应的应用下载页面(apk或App Store)之解决方案
  4. C#LeetCode刷题之#728-自除数(Self Dividing Numbers)
  5. Clion配置Ros环境
  6. mysql day of week_在MySQL中按day_of_week排序
  7. 王道考研操作系统笔记(第一章)附:王道考研408所有PPT和思维导图
  8. Python黑帽子——通过Paramiko使用SSH
  9. 智慧警务三维PGIS及一标三实采集、展示平台
  10. 将2010年的旧电脑升级为Win8.1遇到的问题及解决办法
  11. 中华人民共和国网络安全法
  12. Python 自动化工具开源及办公自动化 10 高频操作,代码可直接套用
  13. double转换成百分数
  14. 深入浅出 React 和 Redux
  15. HTML字体颜色对照表
  16. 华为Mate30系列手机提前发布为哪般?
  17. Kotlin扩展方法进化之Context Receiver
  18. Workbench螺栓连接的模拟方法
  19. 比比网开源的一款头像制作小程序
  20. sql 创建表,批量插入数据

热门文章

  1. button按钮无法提交表单问题发现与解决
  2. 典型的开发国内小项目没失败的经验分享
  3. hive 字符串前有 ’ 符号
  4. Base:一种 Acid 的替代方案
  5. python scrapy 抓取脚本之家文章(scrapy 入门使用简介)
  6. SVN 报错svn: E200014: Checksum mismatch for 。。。。。
  7. Codeforces Round #375 (Div. 2)
  8. Ampzz 2011 Cross Spider 计算几何
  9. 修改/etc/sudoers权限后的补救方法
  10. JAVA基础之理解JNI原理