C++?动态内存管理!!!

一、引言

之前我们一起讨论了类和对象的相关知识,接下来我们将继续完善我们的知识体系,为以后继续深入学习C++知识添砖加瓦,在本期我们将一起学习C++中关于动态内存管理的相关知识,在学习之前将要先回顾C语言中是如何进行动态内存管理的。

二、内存中各个存储分区

1、简介

在内存中有很多不同的分区,比如栈区、堆区、静态区等,不同类型的数据被存储在分区里,不同的分区有不同的性质,方便管理。

2、介绍

内存中不同类型的数据在不同分区,如下图所示:

(1).栈区中存储各种生命周期较短的数据---非静态局部变量、函数参数、函数返回值等,栈是向下增长的

(2).内存映射段是高效的I/O映射方式,用于装载一个共享的动态内存库,用户可以使用系统接口创建共享内存,做进程间通信,在以后的操作系统相关会讨论到

(3).堆区用于程序运行时进行动态内存分配,堆是向上增长的

(4).数据段,又叫静态区,存储生命周期与程序一样长的数据---全局数据和静态数据

(5).代码段,又叫常量区,存储可执行的代码/常量

三、C语言的动态内存管理方式

C语言主要通过malloc/calloc/realloc/free这几个函数进行对内存的申请与释放,它们的使用方式如下:

可以看到:

(1).malloc可以向内存中申请空间,需要传要开的空间大小,单位是字节,不会初始化内容

(2).calloc与malloc类似,需要传要开几个大小为sizeofnum的空间,sizeofnum是第二个参数,会将所有内容初始化为0

(3).realloc可以将之前开辟的空间扩容,传参时第一个参数传入之前动态开辟的指针,第二个参数是想开的空间大小,单位是字节

(4).free用于释放动态开辟的空间,传入指针

(5).以上用于开辟空间的函数返回值都是void*类型,需要进行强制类型转换

(6).事实上还需要对开辟的空间成功与否进行检查,但由于这里只是回顾以下C语言的动态内存管理方式,所以不多赘述了

四、C++的动态内存管理方式

1、引入

C语言的动态内存管理方式对于内置类型来说是足够的,但是对于自定义类型就显得有一些吃力了,这是因为C语言的动态内存管理方式不能很好的对于初始化的对象进行初始化,在之前我们已经一起学习过C++类和对象相关的知识,对于自定义类型来说,是非常需要初始化的,也就是在定义时调用构造函数,怎么办呢?这时候就要使用到接下来要提到的C++动态内存管理方式---new和delete

2、new和delete操作内置类型

先看代码:

new和delete对于内置类型的语法如上所示,接下来总结一下:

(1).new之后跟一个类型表示向内存申请一块空间。如果是一个空间,可以在new后跟一个'()'对它进行初始化,'()'中的内容就是初始化的内容;在new后跟一个'[]'表示向内存申请多个空间,'[]'中就是要开辟该类型空间的个数,不能初始化

(2).new会返回一个对应类型的指针,用户借助该指针对空间进行操作

(3).如果开辟一个单位大小空间使用delete进行释放,如果是多个空间则使用delete[]进行释放,要匹配使用

3、new和delete操作自定义类型

在这里可以体会到malloc/free与new/delete的最大区别:new/delete会调用构造/析构函数,而malloc/free则不会,先看代码:

由以上代码及其运行结果可以看出,new/delete会调用类的默认构造函数/默认析构函数,如果开辟多个空间,那么对于每一个对象都会调用一次,同时由p2也可以看出,在开辟空间的同时也可以传参调用构造函数,如果开辟多个空间同时没有默认构造函数怎么办呢?这时候可以象初始化数组一样使用'{}'对该类对象数组进行初始化,代码如下:

在上面演示了两种这种情况下的处理方式,分别是利用隐式类型转换和匿名类型,都可以选择

五、介绍operator new 和operator delete函数

1、介绍

new和delete是供用户使用的动态内存管理操作符,operator new 和operator delete则是两个库中实现的全局函数,new在底层调用operator new,delete在底层调用operator delete,这两个全局函数是由malloc和free封装而成的,主要进行的改动是将malloc申请内存失败时的返回NULL进行了封装,变为了抛异常,这是由于面向对象的语言更倾向于直接的抛异常,而不是通过返回值

2、全局函数源码

复制代码
 /*
 operator new:该函数实际通过malloc来申请空间,当malloc申请空间成功时直接返回;申请空间失败,
尝试执行空               
间不足应对措施,如果改应对措施用户设置了,则继续申请,否则抛异常。
*/
 void *__CRTDECL operator new(size_t size) _THROW1(_STD bad_alloc)
 {
 // try to allocate size bytes
     void *p;
     while ((p = malloc(size)) == 0)
         if (_callnewh(size) == 0)
         {
             // report no memory
             // 如果申请内存失败了,这里会抛出bad_alloc 类型异常
             static const std::bad_alloc nomem;
             _RAISE(nomem);
         }
     return (p);
 }

 /*
 operator delete: 该函数最终是通过free来释放空间的
*/
 void operator delete(void *pUserData)
 {
     _CrtMemBlockHeader * pHead;
     RTCCALLBACK(_RTC_Free_hook, (pUserData, 0));
     if (pUserData == NULL)
         return;
     _mlock(_HEAP_LOCK);  /* block other threads */
     __TRY
         /* get a pointer to memory block header */
         pHead = pHdr(pUserData);
         /* verify block type */
         _ASSERTE(_BLOCK_TYPE_IS_VALID(pHead->nBlockUse));
         _free_dbg( pUserData, pHead->nBlockUse );
     __FINALLY
         _munlock(_HEAP_LOCK);  /* release other threads */
     __END_TRY_FINALLY
     return;
 }   

3、总结

通过上述两个全局函数的实现知道,operator new 实际也是通过malloc来申请空间,如果malloc申请空间 成功就直接返回,否则执行用户提供的空间不足应对措施,如果用户提供该措施就继续申请,否则就抛异 常。operator delete 最终是通过free来释放空间的。

六、new和delete的实现原理

1、对于内置类型

对于内置类型new/new[]/malloc会开辟指定大小的空间,delete/delete[]/free会释放指定空间,不同的是new/new[]在申请失败时会抛异常,而malloc申请失败时会返回NULL

2、对于自定义类型

(1).new的原理

(1).1.调用operator new函数申请空间

(1).2.调用构造函数,对对象进行构造

(2).delete的原理

(2).1.调用operator delete函数释放空间

(2).2.调用析构函数完成资源的清理

(3).new T[N]的原理

(3).1.调用operator new[]函数,进而调用operator new函数完成N个函数空间的申请

(3).2.调用N次构造函数,完成N个对象的构造

(4).delete[]的原理

(4).1.在目标空间的对象调用N次析构函数,完成N个对象的清理

(4).2.调用operator delete[]释放空间,进而调用operator delete对N个空间进行释放

六、对比malloc/free与new/delete

malloc/free与new/deletw都是在堆上申请空间,它们的区别是:

1、malloc和free是函数,new和deletw是操作符

2、malloc申请的空间不会初始化,new可以初始化

3、malloc要主动计算大小并对返回值进行强制类型转换,new只需要在后面加上类型名也不需要强转

4、malloc申请空间失败时返回NULL而new申请失败时会抛异常

5、在操作自定义类型时,malloc/free不会调用析构函数/构造函数,new和delete则会

七、结语

这就是本期关于C++中动态内存管理相关的所有内容了,希望对大家有所帮助,感谢各位于晏、亦菲的阅读,欢迎大家和我一起讨论、进步。

相关推荐
席万里3 分钟前
Go语言企业级项目使用dlv调试
服务器·开发语言·golang
jerry60920 分钟前
c++流对象
开发语言·c++·算法
fmdpenny21 分钟前
用python写一个相机选型的简易程序
开发语言·python·数码相机
极客智谷23 分钟前
深入理解Java线程池:从原理到实战的完整指南
java·后端
我的耳机没电了23 分钟前
mySpace项目遇到的问题
后端
虾球xz25 分钟前
游戏引擎学习第247天:简化DEBUG_VALUE
c++·学习·游戏引擎
海盗强1 小时前
Babel、core-js、Loader之间的关系和作用全解析
开发语言·前端·javascript
猿榜编程1 小时前
python基础-requests结合AI实现自动化数据抓取
开发语言·python·自动化
陈随易1 小时前
长跑8年,Node.js框架Koa v3.0终发布
前端·后端·程序员
lovebugs1 小时前
Redis的高性能奥秘:深入解析IO多路复用与单线程事件驱动模型
redis·后端·面试