在C++类的结构中可以使用类方法创建内存,使用类的析构函数去施放内存,但有这么一种情况会导致:即使在析构函数中释放了内存,但由于析构函数没有被调用而导致内存泄漏,如下代码。

 1 #include <iostream>
2 #include <string>
3
4 using namespace std;
5
6 class Father
7 {
8 public:
9 Father(const char* addr = "Father 带参构造")
10 {
11 cout << "执行了 Father 的构造函数" << endl;
12 int len = strlen(addr) + 1;
13 this->addr = new char[len];
14 strcpy_s(this->addr, len, addr);
15 }
16
17 ~Father()
18 {
19 cout << "执行了 Father 的析构函数" << endl;
20 if (this->addr)
21 {
22 delete addr;
23 addr = NULL;
24 }
25 }
26 private:
27 char *addr;
28 };
29
30
31 class Son :public Father
32 {
33 public:
34 Son(const char *game = "Son 带参构造", const char *addr = "继承 Father 带参构造" ) :Father(addr)
35 //Son(const char* game = "Son 带参构造")
36 {
37 cout << "执行了 Son 的构造函数" << endl;
38 int len = strlen(game) + 1;
39 this->game = new char[len];
40 strcpy_s(this->game, len, game);
41 }
42 ~Son()
43 {
44 cout << "执行了 Son 的析构函数" << endl;
45 if (this->game)
46 {
47 delete game;
48 game = NULL;
49 }
50 }
51
52 private:
53 char* game;
54 };
55
56 int main()
57 {
58 cout << "--------case 1----------" <<endl;
59 Father* father = new Father();
60 delete father;
61
62 cout << "\n--------case 2----------" << endl;
63 Son* son = new Son();
64 delete son;
65
66 cout << "\n--------case 3----------" << endl;
67 father = new Son();
68 delete father;
69
70 return 0;
71 }

运行结果:

以上打印,case 1和case 2都正常。再看67行代码,在看 case 3 的打印,会发现少释放一个 Son ,

而解决这个问题也很简单,讲父类的析构函数修饰为虚函数便可。还是上边的代码,第17行,父类析构函数前增加 virtual:

 1 #include <iostream>
2 #include <string>
3
4 using namespace std;
5
6 class Father
7 {
8 public:
9 Father(const char* addr = "Father 带参构造")
10 {
11 cout << "执行了 Father 的构造函数" << endl;
12 int len = strlen(addr) + 1;
13 this->addr = new char[len];
14 strcpy_s(this->addr, len, addr);
15 }
16
17 virtual ~Father()
18 {
19 cout << "执行了 Father 的析构函数" << endl;
20 if (this->addr)
21 {
22 delete addr;
23 addr = NULL;
24 }
25 }
26 private:
27 char *addr;
28 };
29
30
31 class Son :public Father
32 {
33 public:
34 Son(const char *game = "Son 带参构造", const char *addr = "继承 Father 带参构造" ) :Father(addr)
35 //Son(const char* game = "Son 带参构造")
36 {
37 cout << "执行了 Son 的构造函数" << endl;
38 int len = strlen(game) + 1;
39 this->game = new char[len];
40 strcpy_s(this->game, len, game);
41 }
42 ~Son()
43 {
44 cout << "执行了 Son 的析构函数" << endl;
45 if (this->game)
46 {
47 delete game;
48 game = NULL;
49 }
50 }
51
52 private:
53 char* game;
54 };
55
56 int main()
57 {
58 cout << "--------case 1----------" <<endl;
59 Father* father = new Father();
60 delete father;
61
62 cout << "\n--------case 2----------" << endl;
63 Son* son = new Son();
64 delete son;
65
66 cout << "\n--------case 3----------" << endl;
67 father = new Son();
68 delete father;
69
70 return 0;
71 }

打印结果:

申请的内存均正确的释放。

结论:当我们把父类的析构函数定义为虚函数,这种修饰不是为了重写,他会让 Father 类(基类)的指针进行 Delete 操作时使用动态析构(如果这个指针指向的是子类对象,那么会先调用该子类的析构函数,然后在调用自己类的析构函数)。

养成习惯:为了防止内存泄漏,最好是在基类的析构函数上添加关键字 virtual ,使基类析构函数为虚函数。

======================================================================================================================

C++ 消失的析构函数 —— virtual 实现的动态析构的更多相关文章

  1. 析构函数virtual与非virtual区别 [转]

    作为通常的原则,如果一个类定义了虚函数,那么它的析构函数就应当是virtual的.因为定义了虚函数则隐含着:这个类会被继承,并且会通过基类的指针指向子类对象,从而得到多态性.   这个类可能会被继承, ...

  2. effective c++:virtual函数在构造函数和析构函数中的注意事项

    如不使用自动生成函数要明确拒绝 对于一个类,如果你没有声明,c++会自动生成一个构造函数,一个析构函数,一个copy构造函数和一个copy assignment操作符. class Empty { p ...

  3. 为什么内联函数,构造函数,静态成员函数不能为virtual函数

    http://blog.csdn.net/freeboy1015/article/details/7635012 为什么内联函数,构造函数,静态成员函数不能为virtual函数? 1> 内联函数 ...

  4. C++虚析构函数解析

    当派生类对象从内存中撤销时一般先运行派生类的析构函数,然后再调用基类的析构函数. 如果用new运算符建立的派生类的临时对象,对指向基类的指针指向这个临时对象当用delete运算符撤销对象时,系统执行的 ...

  5. 虚析构函数? vptr? 指针偏移?多态数组? delete 基类指针 内存泄漏?崩溃?

    五条基本规则: 1.如果基类已经插入了vptr, 则派生类将继承和重用该vptr.vptr(一般在对象内存模型的顶部)必须随着对象类型的变化而不断地改变它的指向,以保证其值和当前对象的实际类型是一致的 ...

  6. 虚函数(virtual)为啥不能是static

    静态成员函数,可以不通过对象来调用,即没有隐藏的this指针. virtual函数一定要通过对象来调用,即有隐藏的this指针. static成员没有this指针是关键!static function ...

  7. C++回顾day03---<纯虚函数和抽象类以及虚析构函数,delete使用>

    一:纯虚函数和抽象类 纯虚函数是一个在基类中说明的虚函数,在基类中没有定义,要求任何派生类都定义自己的版本 纯虚函数为各个派生类提供一个公共接口 纯虚函数的形式: virtual 类型 函数名(参数列 ...

  8. C++ 在继承中使用virtual

    使用virtual:如果方法是通过引用类型或指针而不是对象调用的,它将确定使用哪一种方法.如果没有使用关键字irtual,程序将根据引用类型或指针类型选择方法:如果使用了irtual,程序将根据引用或 ...

  9. C++学习---- virtual的三种用法

    virtual用法一:多态 #include<iostream> using namespace std; class A{ public: virtual void display(){ ...

随机推荐

  1. LeetCode283移动零问题java高效解法

    一.题目: 给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序. 示例: 输入: [0,1,0,3,12] 输出: [1,3,12,0,0] 说明: 1.必须 ...

  2. Jmeter(二十七) - 从入门到精通 - Jmeter Http协议录制脚本(详解教程)

    1.简介 LoadRunner的录制功能让性能测试脚本编写对于不懂代码的人变成了一件容易上手的事,但是由于LoadRunner收费高昂,庞大,一般企业很少用,除非必须使用.Jmeter作为性能测试中的 ...

  3. guitar pro系列教程(七):Guitar Pro丰富的演奏技巧

    这一章,我们来讲guitar pro的演奏技巧 上一章节我们了解了Guitar Pro的音轨菜单,对于玩吉他的小伙伴肯定比较想要了解其的演奏方面的技巧,毕竟我们使用这款软件就是希望自己在吉他方面有更多 ...

  4. 一 HTML基础入门

    HTML概念 HTML是标记语言,由W3C组织提供的一套标记标签组成.其使用标记标签来描述网页,一个网页除了由大量的标签组成,还有后续要学习的css样式和JavaScript脚本组合而成. 网页与网站 ...

  5. jQuery 第三章 CSS操作

    .css() .attr() .prop() .css() 参数填法:如下所示 ↓  可填px 可不填,注意点:background-color  这类属性,需要填成 小驼峰式  background ...

  6. 【Redis】【报错】redis.exceptions.ResponseError: DENIED Redis is running in protected mode

    (一)报错前提 写flask 项目的时候,因为连接了私有云中的redis地址指定了IP host,启动项目的时候报错 (二)解决方法 首先要切换到root用户 root@:/etc/redis# pw ...

  7. 【ES6】ES6入门笔记

    1.概要 - ECMAScript2015(ES6)是Javascript最标准的语法式样,是在2015年6月由Ecma国籍组织公布的最新版本,现在已经被多个领域和浏览器所广泛采纳和使用. 2.学习网 ...

  8. Java基础教程——安装JDK

    视频讲解:https://www.bilibili.com/video/av48196406/?p=3 使用[jdk-8u144-windows-x64.exe] 下载地址: 链接:https://p ...

  9. 如何让文科生5分钟写上Python

    序言 这篇文章是转型后发表的第一篇文章,有必要先聊一下我的写文计划. 串行文章和并行文章 我会按照发文顺序分为串行文章和并行文章.Python 语言本身的内容,我会按照入门.进阶.原理的顺序依次展开. ...

  10. 一周一个中间件-hbase

    前言 hbase是大数据的生态的一部分,是高可靠性.高性能.列存储.可伸缩.实时读写的数据库系统.介于nosql和RDBMS之间.主要存储非结构化和半结构化的松散数据. 海量数据存储 快速随机访问 大 ...