C++:new与delete

hello,各位小伙伴,本篇文章跟大家一起学习《C++:new与delete》,感谢大家对我上一篇的支持,如有什么问题,还请多多指教 !

文章目录

  • :rocket: C++内存管理
    • :airplane: 初识`new`和`delete`
    • :airplane: `new`和`delete`底层逻辑
    • :airplane: 数组`new[]`和`delete[]`注意事项
    • :airplane: `new`和`delete`的实现原理
    • :airplane: 定位new表达式(placement-new) (现阶段了解一下即可)
    • :airplane: malloc/free和new/delete的区别
    • :airplane: 关于内存泄漏
      • 1.**:fire:内存泄漏的危害!!!**
      • 2.:fire:内存泄漏的分类(了解一下即可)
      • 3.:fire:如何检测内存泄漏(了解一下)

🚀 C++内存管理

我们学过malloc、calloc、realloc、free,这些都是我们在C语言时学的,那么C++又引入了什么呢?

✈️ 初识newdelete

没错,就是newdelete,他们都是用于动态内存管理的操作符。

  • new 用于在堆内存中动态地分配内存空间,通常用于创建对象或数组。
  • delete 用于释放由 new 分配的内存空间,以防止内存泄漏。

以下是它们的基本用法:

// 使用 new 分配单个对象的内存空间
int* ptr = new int;// 使用 new 分配数组的内存空间
int* arr = new int[10];// 使用 delete 释放单个对象的内存空间
delete ptr;// 使用 delete[] 释放数组的内存空间
delete[] arr;

🔥在使用 new 分配内存后,必须使用 deletedelete[] 来释放这些内存,否则会导致内存泄漏。

当然,我们还可以在new时进行初始化,如:

int* ptr = new int(1);
int* arr = new int[10]{1,2,3,4,5,6,7,8,9,0};

但是对于数组,我们一般用循环初始化,毕竟数组的元素个数可多可少

🔥同样是在堆上进行操作,那么newdelete相比于mallocfree又有什么优势呢?

  1. 构造函数和析构函数的调用

    • 使用 new 分配的内存会调用对象的构造函数,而释放内存时会调用对象的析构函数。
    • 使用 malloc() 分配的内存不会调用对象的构造函数和析构函数,它仅分配内存空间。
    • 因此,如果你在 C++ 中使用类,尤其是含有构造函数和析构函数的类,应该优先使用 newdelete,以确保对象的生命周期正确管理。
  2. 类型安全

    • newdelete 是类型安全的,它们会自动为分配和释放的内存调用正确的构造函数和析构函数。
    • malloc()free() 不是类型安全的,它们仅仅操作指针和内存地址,不关心数据类型。
  3. 数组分配

    • new[]delete[] 用于分配和释放数组,它们可以正确处理数组的元素。
    • malloc()free() 无法直接处理数组,你需要手动跟踪分配的内存大小,并确保正确释放。
  4. 内存对齐

    • newnew[] 会保证分配的内存按照对象的对齐要求进行,而 malloc() 则不保证内存对齐。
    • 内存对齐在某些情况下可能会影响性能和内存访问的正确性。

总的来说,在 C++ 中,如果你在使用类或动态分配数组,最好使用 newdelete,因为它们更符合 C++ 的对象模型,并且提供了更多的类型安全和便利性。

还有

  • new不需要强制类型转换
  • new不需要计算需要多大内存空间

✈️ newdelete底层逻辑

🔥 operator newoperator delete函数

newdelete是用户进行动态内存申请和释放的操作符,operator new operator delete是系统提供的全局函数,new在底层调用operator new全局函数来申请空间,delete在底层通过operator delete全局函数来释放空间。

看下述代码:

class A
{
public:A(int a = 1):_a(a){cout << "A()" << endl;}~A(){cout << "~A()" << endl;}
private:int _a;
};int main()
{A* ptr = (A*)operator new(sizeof(A));new(ptr)A;// 调用构造函数,不能直接调用ptr->~A();// 调用析构函数,能直接调用return 0;
}

operator new该函数实际通过malloc来申请空间,当malloc申请空间成功时直接返回;申请空间失败,尝试执行空间不足应对措施,如果改应对措施用户设置了,则继续申请,否则抛异常
operator delete该函数最终是通过free来释放空间的。

new(ptr)A;// 调用构造函数,不能直接调用
ptr->~A();// 调用析构函数,能直接调用

由于operator delete不会调用构造函数和析构函数,要手动操作。
调用构造函数时也可以传参:

new(ptr)A(1);

✈️ 数组new[]delete[]注意事项

🔥对于delete[]delete一定要对应使用
对于动态分配的内存,在释放时必须使用与分配时相对应的操作符。

  • 对于使用new分配的单个对象,应使用delete释放内存。
  • 对于使用new[]分配的数组,应使用delete[]释放内存。

使用delete释放使用new[]分配的内存或使用delete[]释放使用new分配的内存都会导致未定义的行为。这是因为newnew[]分别调用了不同的构造函数,因此释放时必须相应地使用deletedelete[]来调用相应的析构函数。

例如,如果你这样分配内存:

int* a= new int;
int* arr= new int[10];

则应该这样释放内存:

delete a;
delete[] arr;

这样做可以确保内存被正确释放,避免内存泄漏和其他潜在的问题。

🔥对于自定义类型,还有一些要注意的点
我们来看下述代码1:

class A
{
public:A(int a = 1):_a(a){cout << "A()" << endl;}~A(){cout << "~A()" << endl;}
private:int _a;
};int main()
{A* ptr = new A[10];return 0;
}

大家猜猜系统为开辟的数组一共用了多大的内存(32位机器)
答案是:44字节
在这里插入图片描述
我们再来看看这个代码2:

int main()
{int* arr = new int[10];return 0;
}

大家再猜猜系统为开辟的数组一共用了多大的内存(32位机器)
答案是:40字节
在这里插入图片描述

这就奇怪了呀,明明都是开10个int类型的数组,为什么?

这是因为自定义类型中有析构函数,那么编译器会认为这个析构函数是需要使用的,但是要析构多少次,就要多开4个字节(32位机器地址大小为4个字节,64位地址大小为8字节)来存储有多少个元素需要进行析构,如图:
在这里插入图片描述

那么如果该自定义类型没有析构函数,是不是就变成40字节了?
答案是:正确的

在这里插入图片描述

✈️ newdelete的实现原理

🔥对于内置类型:
如果申请的是内置类型的空间,newmallocdeletefree基本类似,不同的地方是:new/delete申请和释放的是单个元素的空间,new[]delete[]申请的是连续空间,而且new在申请空间失败时会抛异常malloc会返回NULL

🔥对于自定义类型:

  • new的原理
  1. 调用operator new函数申请空间
  2. 在申请的空间上执行构造函数,完成对象的构造在这里插入图片描述
  • delete的原理
  1. 在空间上执行析构函数,完成对象中资源的清理工作
  2. 调用operator delete函数释放对象的空间在这里插入图片描述
  • new T[N]的原理
  1. 调用operator new[]函数,在operator new[]中实际调用operator new函数完成N个对象空间的申请
  2. 在申请的空间上执行N次构造函数在这里插入图片描述
  • delete[]的原理
  1. 在释放的对象空间上执行N次析构函数,完成N个对象中资源的清理
  2. 调用operator delete[]释放空间,实际在operator delete[]中调用operator delete来释放空间在这里插入图片描述

✈️ 定位new表达式(placement-new) (现阶段了解一下即可)

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

  • 使用格式:
    new (place_address) type或者new (place_address) type(initializer-list)
    place_address必须是一个指针,initializer-list是类型的初始化列表
  • 使用场景:
    定位new表达式在实际中一般是配合内存池使用。因为内存池分配出的内存没有初始化,所以如果是自定义类型的对象,需要使用new的定义表达式进行显示调构造函数进行初始化。
class A
{
public:A(int a = 1):_a(a){cout << "A()" << endl;}~A(){cout << "~A()" << endl;}
private:int _a;
};int main()
{
// p1现在指向的只不过是与A对象相同大小的一段空间,还不能算是一个对象,因为构造函数没有执行A* ptr = (A*)malloc(sizeof(A));new(ptr)A(10);// 注意:如果A类的构造函数有参数时,此处需要传参,如我所写free(ptr);A* ptr = (A*)operator new(sizeof(A));new(ptr)A(10);operator delete (ptr);return 0;
}

✈️ malloc/free和new/delete的区别

🔥malloc/freenew/delete的共同点是:都是从堆上申请空间,并且需要用户手动释放。不同的地方是:

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

✈️ 关于内存泄漏

1.🔥内存泄漏的危害!!!

内存泄漏是指程序在动态分配内存后,无法再次访问或释放该内存,导致程序持续占用内存而不释放。内存泄漏可能会导致以下危害:

  1. 内存资源浪费:内存泄漏会导致程序持续占用内存而不释放,随着时间的推移,系统的可用内存将逐渐减少,可能导致系统性能下降甚至崩溃。

  2. 程序性能下降:随着内存泄漏的累积,系统可用内存减少,可能会导致频繁的内存交换(如果系统使用虚拟内存),增加了页面调度的开销,降低了程序的整体性能。

  3. 程序崩溃:当程序持续占用内存而不释放,最终可能导致系统内存耗尽,触发操作系统的内存管理机制,导致程序崩溃或被系统强制终止。

  4. 资源管理混乱:内存泄漏可能会导致程序中的资源管理混乱,例如无法及时释放打开的文件、数据库连接或网络连接,进而影响系统的稳定性和可靠性。

  5. 难以调试和定位问题:内存泄漏通常是程序中较为隐蔽的问题,随着内存使用的增加,程序的运行速度可能会变慢,但不一定会立即导致崩溃或错误,因此难以定位和调试。

为了避免内存泄漏的危害,程序员应该养成良好的内存管理习惯,及时释放不再需要的内存,并使用内存检测工具(如Valgrind、AddressSanitizer等)来帮助检测和修复潜在的内存泄漏问题。

小伙伴们一定要记得噢!!!

2.🔥内存泄漏的分类(了解一下即可)

C/C++程序中一般我们关心两种方面的内存泄漏:

  • 堆内存泄漏(Heap leak)

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

  • 系统资源泄漏

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

3.🔥如何检测内存泄漏(了解一下)

在vs下,可以使用windows操作系统提供的_CrtDumpMemoryLeaks() 函数进行简单检测,该函数只报出
了大概泄漏了多少个字节,没有其他更准确的位置信息。

int main()
{int* p = new int[10];// 将该函数放在main函数之后,每次程序退出的时候就会检测是否存在内存泄漏_CrtDumpMemoryLeaks();return 0;
}
// 程序退出后,在输出窗口中可以检测到泄漏了多少字节,但是没有具体的位置
Detected memory leaks!
Dumping objects ->
{79} normal block at 0x00EC5FB8, 40 bytes long.
Data: < > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD
Object dump complete.

咱目前只是了解一下,无需理解

因此写代码时一定要小心,尤其是动态内存操作时,一定要记着释放。但有些情况下总是防不胜防,简单的
可以采用上述方式快速定位下。如果工程比较大,内存泄漏位置比较多,不太好查时一般都是借助第三方内
存泄漏检测工具处理的。

  • 在linux下内存泄漏检测:linux下几款内存泄漏检测工具
  • 在windows下使用第三方工具:VLD工具说明
  • 其他工具:内存泄漏工具比较

好啦,这篇文章就到此结束了
所以你学会了吗?

好啦,本章对于《C++:new与delete》的学习就先到这里,如果有什么问题,还请指教指教,希望本篇文章能够对你有所帮助,我们下一篇见!!!

如你喜欢,点点赞就是对我的支持,感谢感谢!!!

请添加图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/2228.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

海康智能相机FTP本地存图流程

背景&#xff1a;近期一个新项目需要使用到智能相机&#xff0c;借助智能相机算法直接输出检测结果并将相机图像进行本地化保存和展示。由于申购目标智能相机未到&#xff0c;暂时使用测试智能相机。 目标智能相机型号&#xff1a;海康智能相机MV-SC3050XC 当前测试相机型号…

autodesk系列软件安装错误1603,手动安装Autodesk Desktop Licensing Service之后,启动服务提示错误1067

一般Autodesk Desktop Licensing Service这个服务没安装或者不正常会导致autodesk系列软件安装错误1603或者其他报错。 手动安装Autodesk Desktop Licensing Service之后&#xff0c;启动服务提示错误1067&#xff0c; 解决方法如下 打开autoremove点击扩展功能&#xff0c;输…

基于CAPL的S19文件解析

&#x1f345; 我是蚂蚁小兵&#xff0c;专注于车载诊断领域&#xff0c;尤其擅长于对CANoe工具的使用&#x1f345; 寻找组织 &#xff0c;答疑解惑&#xff0c;摸鱼聊天&#xff0c;博客源码&#xff0c;点击加入&#x1f449;【相亲相爱一家人】&#x1f345; 玩转CANoe&…

UDS报文传输的四种帧

ISO14229-1规定了26个诊断服务细节&#xff0c;也就是UDS诊断报文的细节。它只规定了各个服务每个字节的含义&#xff0c;它不关心底层到底是怎么传输的。 ISO15765-2规定了基于CAN总线进行UDS报文传输的细节&#xff08;包括四种帧&#xff09;。是在CAN总线传输的情况下&…

掉落回弹问题(C语言)

一、N-S流程图&#xff1b; 二、运行结果&#xff1b; 三、源代码&#xff1b; # define _CRT_SECURE_NO_WARNINGS # include <stdio.h>int main() {//初始化变量值&#xff1b;float b 100;float sum 0;int i 0;//运算&#xff1b;for (i 1; i < 10; i){//运算&…

力扣HOT100 - 101. 对称二叉树

解题思路&#xff1a; class Solution {public boolean isSymmetric(TreeNode root) {if(root null) return true;return recur(root.left, root.right);}boolean recur(TreeNode L, TreeNode R) {if (L null && R null) return true;if (L null || R null || L.…

前端开发攻略---实现发送手机验证码60s倒计时效果(手机号验证+按钮文字自定义显示+Vue2写法+Vue3写法)

1、演示 2、说明 1、为了便于演示&#xff0c;本示例将在3秒后就再次发送。您可以根据需要自定义此时间间隔。 2、采用最少的变量以满足需求&#xff0c;以减少内存占用。 3、不仅仅局限于按钮情况&#xff0c;也可应用于不禁用按钮的情况&#xff0c;以实现更多的扩展性。 4、…

zkVM选型要点

1. 引言 当选择ZK工具&#xff0c;来做可验证链下计算来扩容区块链时&#xff0c;需考虑&#xff1a; 1&#xff09;为何应选择zkVM&#xff1f;2&#xff09;zkVM有哪些基本功能&#xff1f;3&#xff09;哪些zkVM可提供这些基本功能&#xff1f; 2. 为何应选择zkVM&#x…

大模型培训老师叶梓:通过微调提升小型语言模型的复杂推理能力

在人工智能的快速发展中&#xff0c;复杂推理能力的提升一直是研究者们追求的目标。最近&#xff0c;一项发表在arXiv上的研究成果【1】&#xff0c;提出了一种创新的方法&#xff0c;即通过微调小型语言模型&#xff08;LMs&#xff09;&#xff0c;并将其与大型语言模型&…

贪吃蛇游戏C语言破解:成为编程高手的必修课!

​ 个人主页&#xff1a;秋风起&#xff0c;再归来~ 文章专栏&#xff1a;C语言实战项目 个人格言&#xff1a;悟已往之不谏&#xff0c;知来者犹可追 克心守己&#xff0c;律己则安&#xff01; 1、游戏效果演示 贪吃蛇游戏效果演示 2、win32 A…

20240423给飞凌的OK3588-C开发板适配OV13855【绿屏】linux

20240423给飞凌的OK3588-C开发板适配OV13855【绿屏】 2024/4/22 20:29 开发板&#xff1a;飞凌的OK3588-C OS操作系统&#xff1a;linux R4/Buildroot 【OV13855接到CAM1上&#xff0c;如果要接到CAM2上请修改相关的DTS即可】 https://item.taobao.com/item.htm?_unju3ku2f4…

kerberos:适配华为FI

文章目录 一、hive1、hive thrift连接方式 一、hive 1、hive thrift连接方式 kerberos认证失败信息 缺少配置&#xff1a;{“hadoop.rpc.protection”:“privacy”}&#xff0c;具体可参考&#xff1a;kerbros认证相关问题 华为FI参考资料&#xff1a; https://github.com…

【MySQL 数据宝典】【磁盘结构】- 004 redolog 重做日志

一、背景介绍 持久性要求&#xff1a; 对于已提交的事务&#xff0c;即使系统发生崩溃&#xff0c;其对数据库的更改也不能丢失。问题&#xff1a; 在事务提交前将所有修改的页面刷新到磁盘浪费资源。随机IO导致刷新速度慢。 解决方案&#xff1a; 【数据副本】记录事务执行过…

linux信号机制分析

概念 信号递达&#xff1a;实际执行信号的处理动作就是信号递达 信号未决&#xff1a;信号从产生到递达之间的状态就是信号未决&#xff08;未决就是没有解决&#xff09; 收到某信号后&#xff0c;把未决信号集中的此信号置为1&#xff08;1表示未解决的信号&#xff09;&a…

【Camera Sensor Driver笔记】四、点亮指南之EEPROM配置

很久之前写的一版&#xff1a; 【Qcom Camera】微距eeprom调试_cam_vio-supply <&l7p>-CSDN博客 <slaveInfo> EEPROMName cat24c64_imx585 eeprom型_sensor名字 slaveAddress 0xa0 i2c write address regAddrType …

国产PLC有哪些,哪个牌子比较好用?

你知道国产PLC有哪些吗,哪个牌子更好用吗&#xff1f; 今天拿出国产先锋的汇川与台达对比&#xff0c;注&#xff1a;视频后方有各品牌学习资料免费送&#xff0c;需要的移步自取。话说回来&#xff0c;只要基于Codesys开发的都比较好用&#xff0c;只是使用底层芯片不同&…

国产软件不背黑锅:4款功能强大的黑科技软件,且用且珍惜

国内软件常被冠以“流氓软件、需要额外付费、广告繁多”等负面标签&#xff0c;但实际上&#xff0c;其中不乏一些小众却功能强大、用户体验极佳的软件。 布丁扫描——免费专业的扫描APP&#xff08;安卓、ios&#xff09; 布丁扫描&#xff0c;无疑是我今年的最爱&#xff0…

服务器还在长期泄密,保护数据IPSSL证书必不可少

IP SSL&#xff0c;或称为安全套接层协议&#xff08;Secure Sockets Layer&#xff09;&#xff0c;是一种用于在互联网上进行通信加密的技术标准&#xff0c;它通过为数据提供加密服务&#xff0c;确保了数据在传输过程中的安全与完整。其工作方式是在客户端和服务器之间建立…

你们项目日志是如何处理的???

ELK日志采集系统 1.什么是ELK ELK 是一套流行的数据搜索、分析和可视化解决方案&#xff0c;由三个开源项目组成&#xff0c;每个项目的首字母合起来形成了“ELK”这一术语&#xff1a; Elasticsearch (ES): Elasticsearch 是一个基于 Apache Lucene 构建的分布式、实时搜索与…

Java 字符

Java 字符 Java教程 - Java字符 在Java中&#xff0c;char存储字符。Java使用Unicode来表示字符。Unicode可以表示在所有人类语言中找到的所有字符。 Java char是16位类型。 字符的范围是 0 到 65,536 。没有负字符。 Char文字 Java中的字符是Unicode字符集的索引。字符表…