当前位置:首页 » 《休闲阅读》 » 正文

C++:new与delete

10 人参与  2024年04月28日 10:58  分类 : 《休闲阅读》  评论

点击全文阅读


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又有什么优势呢?

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

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

类型安全

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

数组分配

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

内存对齐

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

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

还有

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

✈️ newdelete底层逻辑

? operator new与operator 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的原理 调用operator new函数申请空间在申请的空间上执行构造函数,完成对象的构造在这里插入图片描述delete的原理 在空间上执行析构函数,完成对象中资源的清理工作调用operator delete函数释放对象的空间在这里插入图片描述new T[N]的原理 调用operator new[]函数,在operator new[]中实际调用operator new函数完成N个对象空间的申请在申请的空间上执行N次构造函数在这里插入图片描述 delete[]的原理 在释放的对象空间上执行N次析构函数,完成N个对象中资源的清理调用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/free和new/delete的共同点是:都是从堆上申请空间,并且需要用户手动释放。不同的地方是:

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

✈️ 关于内存泄漏

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

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

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

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

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

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

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

为了避免内存泄漏的危害,程序员应该养成良好的内存管理习惯,及时释放不再需要的内存,并使用内存检测工具(如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 CDObject dump complete.

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

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

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

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

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

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

请添加图片描述


点击全文阅读


本文链接:http://zhangshiyu.com/post/101419.html

<< 上一篇 下一篇 >>

  • 评论(0)
  • 赞助本站

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

关于我们 | 我要投稿 | 免责申明

Copyright © 2020-2022 ZhangShiYu.com Rights Reserved.豫ICP备2022013469号-1