【C++进阶之路】内存管理

news/2024/10/20 5:20:28/

文章目录

  • 一.内存管理
    • 1. 内存布局
    • 2. C++的内存管理
      • ①内置类型
      • ② 自定义类型
    • 3. operate new 与 operate delete
      • ① 解读operate new源代码
      • ② 解读operate delete源代码
    • 4. new和delete的基本原理
      • ①new对类对象
      • ②delete对类对象
    • 拓展—— 深入理解delete[]和new[]
    • 对比 C和C++内存管理的用法
    • 5. 定位new
      • 基本用法
    • 6. 内存泄漏

一.内存管理

1. 内存布局

在这里插入图片描述

  • 说明:内存映射段是高效的I/O映射方式,用于装载一个共享的动态内存库。用户可使用系统接口创建共享共享内存,做进程间通信。
  • 说明:调用函数的传参是在栈上开辟空间的,且从右向左进行传参。函数的代码的执行也是在栈上的,不过是调用代码区的代码,在栈上执行而已。
  • 更多关于malloc/realloc/calloc/free以及内存空间的证明的信息:内存管理。

2. C++的内存管理

  • 区别于malloc等申请空间的函数,C++引出了new操作符
  • 区别于free释放空间的函数,C++引出了delete操作符
  • 说明: new和delete一定要配套使用

①内置类型

#include<iostream>
using namespace ::std;
int main()
{int* p = new int(3);delete p;return 0;
}

图解:
在这里插入图片描述

② 自定义类型

  • 数组
#include<iostream>
using namespace ::std;
int main()
{int* p = new int[6]{0};delete[] p;return 0;
}

在这里插入图片描述

#include<iostream>
using namespace ::std;
class Date
{
public:Date(int year){cout << "Date" << endl;_year = year;}~Date(){cout << "~Date()" << endl;}
private:int _year;
};
int main()
{Date* p = new Date(1949);delete p;Date* p1 = new Date[4]{1949,2019,2022,2023};delete[] p1;Date* p2 = new Date[4]{ Date(1949),Date(2019),Date(2022),Date(2023) };delete[] p2;return 0;
}

程序运行结果:
在这里插入图片描述
图解:
在这里插入图片描述

  • 说明:对类来说,new一个对象会调用此对象的构造函数进行初始化,然而malloc一个对象仅仅是对一个对象开辟了空间,并没有调用构造函数

3. operate new 与 operate delete

  • 补充:异常,简单地说就是代码执行到某处中断了,无法继续执行,这时的情况就叫异常。
    平常我们在遇到对空指针解引用的情况时,就是出现异常了。
    图:
    在这里插入图片描述

① 解读operate new源代码

void* __CRTDECL operator new(size_t size) _THROW1(_STD bad_alloc)
{void* p;while ((p = malloc(size)) == 0)if (_callnewh(size) == 0){// 如果申请内存失败了,这里会抛出bad_alloc 类型异常static const std::bad_alloc nomem;_RAISE(nomem);}return (p);
}

图解:
在这里插入图片描述

  • 因此:operate new 是一个函数,调用了malloc,并且如果开辟失败会出现异常
  • 为什么要这样做呢?因为面向对象语言不喜欢用返回值来处理问题,而更喜欢使用抛异常,来解决问题,通过异常我们可以判断出现了什么情况的错误。

② 解读operate delete源代码

void operator delete(void* pUserData)
{_CrtMemBlockHeader* pHead;RTCCALLBACK(_RTC_Free_hook, (pUserData, 0));if (pUserData == NULL)return;_mlock(_HEAP_LOCK);__TRYpHead = pHdr(pUserData);_ASSERTE(_BLOCK_TYPE_IS_VALID(pHead->nBlockUse));_free_dbg(pUserData, pHead->nBlockUse);__FINALLY_munlock(_HEAP_LOCK); __END_TRY_FINALLYreturn;
}

图解:
在这里插入图片描述

  • 因此:operate delete释放空间的操作跟free本质上是相同的

那了解了这两个运算符重载,之后我们就可以进一步探究new和delete了。

4. new和delete的基本原理

代码:

#include<iostream>
using namespace ::std;
class Date
{
public:Date(int year){cout << "Date" << endl;_year = year;}~Date(){cout << "~Date()" << endl;}
private:int _year;
};
int main()
{Date* p = new Date(1949);delete p;return 0;
}

①new对类对象

在这里插入图片描述

代码分析:

	Date* p = new Date(1949);

图解:
在这里插入图片描述

②delete对类对象

在这里插入图片描述

代码分析:

	delete p;

图解:
在这里插入图片描述
进入此句汇编代码:
在这里插入图片描述

拓展—— 深入理解delete[]和new[]

  • 讨论范围:类对象数组
  • 在我们创建一个类对象数组时,知道调用多少次构造函数,但在销毁时,我们怎么知道要调用多少次析构函数?
  • 换句话说——就是new[]做了什么,可以让delete[]省去这个找到有多少个对象的操作。

实验代码:

#include<iostream>
using namespace ::std;
int main()
{Date* arr = new Date[4]{1,2,3,4};delete[] arr;return 0;
}
  • 答案很简单,new[]在开辟数组的空间的前4个字节,放了数组的相关信息,有多少个对象,数组的大小等信息,这样delete[]就能通过指针来找到相关信息,从而进行操作。

那这样的代码呢?

#include<iostream>
using namespace ::std;
int main()
{Date* arr = new Date[4]{1,2,3,4};delete arr;return 0;
}
  • 这样delete将不知道调用几次析构函数,从而可能会出现内存泄漏,这是很可怕的现象。
  • 因此:new[] 和delete[]一定要配套使用。

对比 C和C++内存管理的用法

  1. malloc和free是函数,new和delete是操作符
  2. malloc申请的空间不会初始化,new可以初始化
  3. malloc申请空间时,需要手动计算空间大小并传递,new只需在其后跟上空间的类型即可
  4. malloc的返回值为void*, 在使用时必须强转,new不需要,因为new后跟的是空间的类型
  5. malloc申请空间失败时,返回的是NULL,因此使用时必须判空,new不需要,但是new需要捕获异常
  6. 申请自定义类型对象时,malloc/free只会开辟空间,不会调用构造函数与析构函数,而new在申请空间后会调用构造函数完成对象的初始化,delete在释放空间前会调用析构函数完成空间中资源的清理。

5. 定位new

  • 定位new表达式是在已分配的原始内存空间中调用构造函数初始化一个对象

基本用法

#include<iostream>
using namespace ::std;
int main()
{Date* p2 = (Date *)operator new(sizeof(Date));new(p2)Date(1949);//如果有默认构造,括号可以不写。delete p2;return 0;
}
  • 一般我们会在池化技术中使用,就好比原来你家没有厕所,你上厕所,要去公共厕所,比较远,厕所还可能有人,但是现在你家门口造了厕所,别人要用还要通过你同意,这是不是方便了许多,池化技术与此雷同,也是为了提高效率

6. 内存泄漏

  • 内存泄露 memory leak,是指程序在申请内存后,无法释放已申请的内存空间,一次内存泄露危害可以忽略,但内存泄露堆积后果很严重,无论多少内存,迟早会被占光。

看这样一段代码:

#include<iostream>
using namespace ::std;
int main()
{char* p = new char[1024 * 1024 * 1024];return 0;
}

这个程序在编译器上会导致内存泄漏吗?
答案是——不会的。因为编译器会在程序结束时,帮你释放这些空间,这就跟手机卡了,关机再重启的道理是一样的,但是如果是一个24*7小时不停的程序呢?比如服务器,如果存在内存泄漏,如果说大了很快就会被检查出来,但是如果一次只泄漏几个字节,那么这台服务器会越用越卡,最终导致服务器宕机——出事故了,那相关人员就得跟着遭殃…

  • 分类

1.系统资源泄漏
指程序使用系统分配的资源,比方套接字、文件描述符、管道等没有使用对应的函数释放掉,导致系统资源的浪费,严重可导致系统效能减少,系统执行不稳定。
2.堆内存泄漏(Heap leak)
堆内存指的是程序执行中依据须要分配通过malloc / calloc / realloc / new等从堆中分配的一块内存,用完后必须通过调用相应的 free或者delete 删掉。假设程序的设计错误导致这部分内存没有被释放,那么以后这部分空间将无法再被使用,就会产生Heap Leak。

那该如何解决内存泄漏呢?

1.养成良好的编程习惯,对开辟的空间即用即销毁。
2.合理利用检查内存泄漏的工具,通常这类工具比较昂贵。
3.利用RAII思想——资源获取即初始化,或者智能指针——充分地体现了RAII思想。
4.公司内部规范使用内部实现的私有内存管理库。这套库自带内存泄漏检测的功能选项。
5.私有内存管理库,就是公司内部实现的,这套库自带内存检查功能。


http://www.ppmy.cn/news/87934.html

相关文章

Mysql数据库备份 一天一次 保存最新五天 每天凌晨三点备份

Mysql数据库备份 一天一次 保存最新五天 每天凌晨一点三十备份 步骤一 先查看 sudo systemctl status crond 是否存在 不存在执行下面代码 sudo yum install cronie sudo systemctl start crond sudo systemctl enable crond sudo systemctl status crond 步骤二 Cd /home …

SpringBoot与RedisCacheManager整合

本文根据众多互联网博客内容整理后形成&#xff0c;引用内容的版权归原始作者所有&#xff0c;仅限于学习研究使用 SpringBoot 缓存管理器CacheManager 从3.1开始Spring定义了org.springframework.cache.Cache和org.springframework.cache.CacheManager接口来统一不同的缓存技…

A-21S吸金树脂在金矿尾水、镀金废水中回收金的应用

吸金树脂Tulsimer A-21S 一、技术介绍 传统上使用活性碳吸附金子&#xff0c;珍贵的金会被活性碳吸附于表面&#xff0c;再藉由洗涤或直接焚烧以回收金。使用离子交换树脂回收贵金属比活性碳还具有多方面的优势&#xff0c; 因为藉由特殊制造过程中&#xff0c; 我们可以在其结…

linux搭建hadoop环境

1、安装JDK   &#xff08;1&#xff09;下载安装JDK&#xff1a;确保计算机联网之后命令行输入下面命令安装JDK    sudo apt-get install sun-java8-jdk   &#xff08;2&#xff09;配置计算机Java环境&#xff1a;打开/etc/profile&#xff0c;在文件最后输入下面…

悠可集团再获金鼠标3项大奖,自研营销工具助推全渠道数字营销

5月19日,第14届金鼠标数字营销大赛评选结果揭晓,悠可集团斩获3项大奖,其中悠可集团被评为“年度数字营销杰出代理商”,悠可DTC团队自主研发的智能广告投放引擎Turbo Media及点正科技申报的KOL优先自动化工具均荣获“年度最佳数字营销工具”奖项。 据主办方介绍,本届金鼠标数字营…

分享这几个简单好用的手机使用技巧给大家

技巧一&#xff1a;微信的听文字消息功能是一项方便而实用的功能&#xff0c;旨在帮助用户通过语音合成技术将文字消息转化为语音&#xff0c;以便用户可以通过听觉方式收听和理解信息。 这项功能适用于用户在某些情况下无法阅读或不方便阅读文字消息的场景。当用户收到一条文…

卷S人的166页精品Java面试手册,17大java面试系列专题让你全方位暴击大厂Java面试官!

你有面试机会了吗&#xff1f; 近期&#xff0c;肯定有很多小伙伴&#xff0c;投出去的简历HR基本上都是已读不回&#xff0c;甚至都没有任何回复&#xff0c;或者平台默认筛选&#xff0c;你的简历HR根本就看不到。 即使有些小伙伴简历通过&#xff0c;收到面试邀请了&#…

计网之HTTP请求的构造

文章目录 1. form表单请求构造2. ajax请求构造3. Postman的简单使用 常见的构造 HTTP 请求的方式有以下几种: 直接通过浏览器地址栏, 输入一个 URL 就可以构造出一个 GET 请求.直接点击收藏夹, 得到的也是 GET 请求.HTML 中的一些特殊标签也会触发 GET 请求, 如: link, script…