【C++】:内存管理(new和delete)

devtools/2025/2/9 9:39:42/

目录

C++的内存分布

 C++内存管理方式

 new和delete的使用方法

申请内置类型 

申请自定义类型

malloc/free和new/delete的区别

 operator new 和operator delete函数

内存泄漏

内存泄漏分类

如何避免内存泄漏?


C++的内存分布

在内存里面是分好几个区的

  1. 栈又叫堆栈:非静态局部变量/函数参数/返回值等等,栈是向下增长的。
  2. 内存映射段:高效的I/O映射方式,用于装载一个共享的动态内存库。用户可使用系统接口创建共享共享内存,做进程间通信
  3. 堆:用于程序运行时动态内存分配,堆是可以上增长的。
  4. 数据段(静态区):存储全局数据和静态数据。
  5. 代码段(常量区):可执行的代码/只读常量

 C++内存管理方式

提到C++的内存管理方式,我们不得不先提到C语言的内存管理方式 malloc/calloc/realloc/free

C语言:(malloc、free、calloc、realloc)函数讲解-CSDN博客

首先,C语言内存管理的方式在C++中可以继续使用。但有些地方就无能为力而且使用起来比较麻烦,因此C++又提出了自己的内存管理方式:通过new和delete(操作符)进行动态内存管理。

 new和delete的使用方法

申请内置类型 

一、申请单个某类型的空间

	//动态申请单个int类型的空间int* p1 = new int; //申请delete p1; //销毁//动态申请单个int类型的空间并初始化为10int* p1 = new int(10); //申请 + 赋值delete p1; //销毁

等价于:

	//动态申请单个int类型的空间int* p1 = (int*)malloc(sizeof(int)); //申请free(p1); //销毁//动态申请一个int类型的空间并初始化为10int* p1 = (int*)malloc(sizeof(int)); //申请*p1 = 10; //赋值free(p1); //销毁

二、申请多个某类型的空间 

	//动态申请10个int类型的空间int* p2 = new int[10]; //申请delete[] p2; //销毁//动态申请10个int类型的空间并初始化为0到9int* p2 = new int[10]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; //申请 + 赋值delete[] p2; //销毁

 等价于:

	//动态申请10个int类型的空间int* p2 = (int *)malloc(sizeof(int)* 10); //申请free(p2); //销毁//动态申请10个int类型的空间并初始化为0到9int* p2 = (int*)malloc(sizeof(int)* 10); //申请for (int i = 0; i < 10; i++) //赋值{p2[i] = i;}free(p2); //销毁

注意:申请和释放单个元素的空间,使用new和delete操作符;申请和释放连续的空间,使用new[ ]和delete[ ]。

申请自定义类型

#include<iostream>
using namespace std;
class A
{
public:A(int a = 0): _a(a){cout << "A():" << this << endl;}~A(){cout << "~A():" << this << endl;}
private:int _a;
};

一、申请单个类的空间

    A* p1 = new A;    //申请delete p1;        // 销毁

 new 和 delete 调用了构造函数和析构函数

C语言: 

	A* p1 = (A*)malloc(sizeof(A)); //申请free(p1);   //销毁

 malloc和 free 调用了构造函数和析构函数

 二、申请多个类的空间

	A* p2 = new A[3]; //申请delete[] p2; //销毁

 C语言:

A* p2 = (A*) malloc(sizeof(A)* 10);
free(p2);

注意:在申请自定义类型的空间时,new会调用构造函数,delete会调用析构函数,而malloc和free不会。

总结

  1. C++中如果是申请内置类型的对象或是数组,用new/delete和malloc/free没有什么区别。
  2. 如果是自定义类型,区别很大,new和delete分别是开空间+构造函数、析构函数+释放空间,而malloc和free仅仅是开空间和释放空间。
  3. 建议在C++中无论是内置类型还是自定义类型的申请和释放,尽量都使用new和delete 

malloc/free和new/delete的区别

共同点

都是从堆上申请空间,并且需要用户手动释放。

不同点:

  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在释放空间前会调用析构函数完成空间中资源的清理。 
new/deletemalloc/free
new和delete是操作符malloc和free是函数
malloc申请的空间不会初始化new申请的空间会初始化
malloc申请空间时,需要手动计算空间大小并传递new只需在其后跟上空间的类型即可
malloc的返回值是void*,在使用时必须强转new不需要
malloc申请失败时,返回的是NULL,因此使用时必须判空new不需要,但是new需要捕获异常
不会调用构造函数和析构函数会调用构造函数和析构函数

 operator new 和operator delete函数

  1. new 和 delete是用户进行动态内存申请和释放的操作符
  2. operator new 和 operator delete是系统提供的全局函数
  3. new和delete在底层是通过调用全局函数 operator new 和 operator delete来申请和释放空间的。
  4. operator new 和 operator delete的用法和malloc 和 free的用法完全一样,其功能都是在堆上申请和释放空间。
	int* p1 = (int*)operator new(sizeof(int)* 10); //申请operator delete(p1); //销毁

等价于:

	int* p2 = (int*)malloc(sizeof(int)* 10); //申请free(p2); //销毁
  1. operator new的底层是通过调用malloc函数来申请空间的,当malloc申请空间成功时直接返回,malloc 在内存分配失败时返回NULL。operator new在无法分配足够内存时,会抛出std::bad_alloc异常
  2. 而operator delete的底层是通过调用free函数来释放空间的

注意:虽然说operator new和operator delete是系统提供的全局函数,但是我们也可以针对某个类,重载其专属的operator new和operator delete函数,进而提高效率。

内存泄漏

内存泄漏

内存泄漏是指因为疏忽或错误造成程序未能释放已经不再使用的内存的情况。内存泄漏并不是指内存在物理上的消失,而是应用程序分配某段内存后,因为设计错误,失去了对该段内存的控制,因而造成了内存的浪费。

内存泄漏的危害:

长期运行的程序出现内存泄漏,影响很大,如操作系统、后台服务等等,出现内存泄漏会导致响应越来越慢,最终卡死。

内存泄漏分类

1、堆内存泄漏(Heap Leak)

 堆内存指的是程序执行中通过malloc、calloc、realloc、new等从堆中分配的一块内存,用完后必须通过调用相应的free或者delete释放。假设程序的设计错误导致这部分内容没有被释放,那么以后这部分空间将无法再被使用,就会产生Heap Leak。

2、系统资源泄漏

 指程序使用系统分配的资源,比方套接字、文件描述符、管道等没有使用对应的函数释放掉,导致系统资源的浪费,严重可导致系统效能减少,系统执行不稳定。

如何避免内存泄漏?

  1. 工程前期良好的设计规范,养成良好的编码规范,申请的内存空间记住匹配的去释放。
  2. 采用RAII思想或者智能指针来管理资源。
  3. 有些公司内部规范使用内部实现的私有内存管理库,该库自带内存泄漏检测的功能选项。
  4. 出问题了使用内存泄漏工具检测。

内存泄漏非常常见,解决方案分为两种:

  1. 事前预防型。如智能指针等。
  2. 事后查错型。如泄漏检测工具。 

http://www.ppmy.cn/devtools/157316.html

相关文章

深度整理总结MySQL——索引正确使用姿势

索引正确使用姿势 前言MySQL索引优缺点分析✅ 索引的优势⚠️ 索引的代价 如何合理建立索引?——关键原则总结重要的优化机制索引覆盖——通俗的方式讲解索引下推索引跳跃式扫描 前言 这篇文章是补充一些基本概念和实战的一些使用建议. MySQL索引优缺点分析 ✅ 索引的优势 …

Android设置个性化按钮按键的快捷启动应用

设备上硬件按键。除了 Home &#xff0c;Menu&#xff0c;Back &#xff0c;按键。 还有其他按键。 如&#xff1a; F1 按键 &#xff0c;F2按键。 监听F1&#xff0c;和F2的按键。 可以在以下文件查看&#xff0c;记录对应的KeyCode QSSI.13/frameworks/base/services/c…

【AI应用】免费的文本转语音工具:微软 Edge TTS 和 开源版 ChatTTS 对比

【AI论文解读】【AI知识点】【AI小项目】【AI战略思考】【AI日记】【读书与思考】【AI应用】 我试用了下Edge TTS&#xff0c;感觉还不错&#xff0c;不过它不支持克隆声音&#xff08;比如自己的声音&#xff09; 微软 Edge TTS 和 开源版 ChatTTS 都是免费的 文本转语音&…

http状态码:请说说 503 Service Unavailable(服务不可用)的原因以及排查问题的思路

503 Service Unavailable&#xff08;服务不可用&#xff09; 是一种HTTP状态码&#xff0c;表示服务器当前无法处理请求&#xff0c;通常是由于临时性原因导致服务中断。以下是它的常见原因和排查思路&#xff1a; 一、503错误的常见原因 1. 服务器过载 场景&#xff1a;服务…

【真一键部署脚本】——一键部署deepseek

目录 deepseek一键部署脚本说明 0 必要前提 1 使用方法 1.1 使用默认安装配置 1.1 .1 使用其它ds模型 1.2 使用自定义安装 2 附录&#xff1a;deepseek模型手动下载 3 脚本下载地址 deepseek一键部署脚本说明 0 必要前提 linux环境 python>3.10 1 使用方法 1.1 …

基于FPGA的BT1120编解码

BT1120与BT656 类似 BT1120与BT656同类属于一个视频协议,两者无论从组成、协议、同步码以及传输过程都是十分相似: 1、两者都是以F(场)、V(帧)、H(消隐)、D(有效)来区分数据的内容。 2、两者的传输数据都采用一样的方式,即内同步传输数据。 3、两者都传输的数据都是…

SAP FICO科目辅助余额表开发说明书(包括测试样例,源代码仅作参考,不能保证一定可以运行

逻辑说明 筛选屏幕 科目辅助余额表 公司代码会计年度从期间至期间

嵌入式C语言:大小端详解

目录 一、大小端的概念 1.1. 大端序&#xff08;Big-endian&#xff09; 1.2. 小端序&#xff08;Little-endian&#xff09; 二、大小端与硬件体系的关系 2.1. 大小端与处理器架构 2.2. 大小端与网络协议 2.3. 大小端对硬件设计的影响 三、判断系统的大小端方式 3.1.…