
?博客主页: 小羊失眠啦.
?系列专栏:《C语言》 《数据结构》 《C++》 《Linux》 《Cpolar》
❤️感谢大家点赞?收藏⭐评论✍️

文章目录
前言一、内存分布1.1 五大分区1.2 图解 二、重温2.1 malloc/calloc/realloc2.2 free 三、初识3.1 new3.2 delete3.3 特点 四、探究4.1 封装实现4.2 代码展示 五、new/delete 实现步骤5.1 内置类型5.2 自定义类型 六、定位new6.1 应用场景
C++中的内存管理机制和C语言是一样的,但在具体内存管理函数上,C语言的malloc已经无法满足C++面向对象销毁的需求,于是祖师爷在C++中新增了一系列内存管理函数,即 new 和 delete
著名段子:如果你还没没有对象,那就尝试 new 一个吧
前言
将内存分成不同区域是为了实现更好的管理,比如在我们现实生活中,一幢房子会被分为客厅、厨房、卧室、卫生间等区域,目的是为了使我们生活更加方便、空间利用更加合理,计算机也是如此,更何况是空间非常珍贵的内存,因此在我们的程序中存在不同内存分区
一、内存分布
在程序中存在五大分区,各个分区各司其职,比如我们耳熟能详的栈区、堆区、静态
1.1 五大分区
栈区:
栈又称做堆栈,用于存储非静态局部变量、函数参数、返回值等,栈的空间是向下增长的 内存映射段:
内存映射段是高效的I/O映射方式,用于装载一个共享的动态内存库。用户可使用系统接口创建共享共享内存,做进程间通信 堆区:
堆用于程序运行时动态内存分配,堆是可以上增长的,我们的动态内存就是在堆上申请的 数据段:
数据段中负责存储全局数据和静态数据 代码段:
代码段中存储可执行的代码/只读常量 注意: 内存中还存在内核空间,但我们普通用户代码无法读写
1.2 图解

二、重温
先简单回顾下C语言中的动态内存管理
2.1 malloc/calloc/realloc
C语言提供了三种动态内存管理函数
malloc:申请指定大小的空间
int* pi = (int*)malloc(sizeof(int) * 1);//申请一个整型double* pd = (double*)malloc(sizeof(double) * 2);//申请两个浮点型char* pc = (char*)malloc(sizeof(char) * 3);//申请三个字符型 注意: malloc申请的空间都是未初始化的,即被编译器置为随机值
calloc:将申请的空间初始化为 0
int* pi = (int*)calloc(1, sizeof(int));//申请一个整型double* pd = (double*)calloc(2, sizeof(double));//申请两个个浮点型char* pc = (char*)calloc(3, sizeof(char));//申请三个字符型 注意: calloc参数列表与malloc不同,同时calloc申请的空间会被初始化为 0
realloc:对已申请的空间进行扩容
int* tmp = (int*)realloc(pi, sizeof(int) * 10);//将 pi 扩容为十个整型pi = tmp;//常规使用方法 注意: 我们要对所有的申请函数进行空指针检查,预防野指针问题
堆区的空间由我们管理,编译器很信任我们,因此我们要做到有借有还,再借不难
凡是动态开辟的空间,用完后都需要释放
2.2 free
C语言提供的空间释放函数是free
free(tmp);//此时tmp指向pi扩容后的空间,释放tmp就行了tmp = pi = NULL;//两者都需要置空free(pd);pd = NULL;free(pc);//只要是动态开辟的,都需要通过 free 释放pc = NULL; 注意: 只有动态开辟的空间才能使用 free,同时一块空间不能释放两次。我们在 free 后通常会把指针置空
关于C语言动态内存管理更多细节可以看看这篇文章:《C语言动态管理》
C语言 中管理函数只能对内置类型使用,而 C++ 中存在很多自定义类型,常规 malloc 等函数无能为力
三、初识
出现了新的关键字:new 和 delete,它们也有很多形式和使用细节
3.1 new
使用
int* pi = new int;//申请一个整型double* pd = new double(3.14);//申请一个浮点型并初始化为 3.14char* pc = new char[5] {'H', 'e', 'l', 'l', 'o'};//申请五个字符型,并分别初始化为 Helloc 注意:
new 与 malloc等不同,不需要进行空指针检查,也不需要进行类型转换new 的使用极其简单 特点:
new可以用于自定义类型动态开辟时,会调用自定义类型的构造函数 //假设存在日期类Date* ptr = new Date[5];//申请五个日期类,这些类都在堆上 下面来看看C++中的内存释放函数
3.2 delete
形式:
delete 指针delete[] 指针 使用:
C语言中的free 可以用于释放所有动态申请函数,而 C++ 不行,申请与释放需要配套使用 int* pi = new int;delete pi;//直接释放double* pd = new double[5];delete[] pd;//释放五次Date* ptr = new Date[5];delete[] ptr;//释放五次,即调用五次日期类的析构函数 注意:
需要配套使用,new int 搭配 delete,而 new int[] 需要搭配 delete[] 特点:
delete可以用于自定义类型调用销毁时,会先调用自定义类型的析构函数 3.3 特点
C语言和C++动态内存管理函数的最大区别是: 是否会调用自定义类型的构造函数和析构函数
C语言明显不会,毕竟那时候还没有这些概念,而 C++ 作为面向对象的语言,调用构造与析构函数是必然的
C语言中的申请函数不能通过C++的释放函数进行释放,同理C++的申请空间也不能通过C语言的释放函数进行释放,比如下面这些情况是不合理的,可能引发问题
int* cPi = (int*)malloc(sizeof(int));delete cPi;//不合理的操作int* cppPi = new int;free(cppPi);//这样也是不合理的Date* ptr = new Date;free(ptr);//此时会报错,因为 free 并不会调用析构函数 切记,申请与释放要配套使用
四、探究
为何C++中的动态内存管理函数能做到调用构造和析构函数呢?
C++中的封装 4.1 封装实现
new 和 delete 是用户进行动态内存申请和释放的 操作符,它们在实现时会去调用真正的全局函数 operator new 与 operator delete,具体调用情况如下所示:
new 与 new []
new 调用 operator newnew [] 调用 operator new[] delete 与 delete []
delete 调用 operator deletedelete [] 调用 operator delete[] 注意: operator new[] 最终是调用 operator new,operator delete[] 最终也是调用 operator delete
所以严格来说,operator new 和 operator delete 才是我们探讨的主角
所以严格来说,operator new 和 operator delete 才是我们探讨的主角
4.2 代码展示
/*operator new:该函数实际通过malloc来申请空间,当malloc申请空间成功时直接返回;申请空间失败,尝试执行空间不足应对措施,如果改应对措施用户设置了,则继续申请,否则抛异常。*/void* __CRTDECLoperatornew(size_tsize)_THROW1(_STDbad_alloc){// try to allocate size bytesvoid* p;while ((p = malloc(size)) == 0)if (_callnewh(size) == 0){// report no memory//如果申请内存失败了,这里会抛出bad_alloc类型异常staticconststd::bad_allocnomem;_RAISE(nomem);}return(p);} 可以看到,其实 operator new 就是通过对 malloc 的封装实现的,不过进行了改进,当对象为自定义类型时,会去调用它的构造函数,并且当开辟失败时,会抛出异常
再来看看 operator delete 的代码实现
/*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_FINALLYreturn;}/*free的实现*/#define free(p) _free_dbg(p, _NORMAL_BLOCK) operator delete 的代码中也有 free 的影子,当释放对象为自定义类型时,会调用它的析构函数
五、new/delete 实现步骤
下面再来看看两者具体的实现步骤
5.1 内置类型
对于内置类型来说,使用 malloc/free 和 new/delete 没什么区别
5.2 自定义类型
对于自定义类型,new/delete 的实现步骤如下:
new
operator new 申请空间在申请的空间上调用构造函数 delete
operator delete 释放空间 new []
operator new[] 函数,根据数值N,调用N次 operator new 函数申请空间在申请的空间上调用N次构造函数 delete []
operator delete[] 函数,然后由函数再调用 operator delete 释放空间 六、定位new
定位new 是 new 的新用法
目的:
对已开辟而未初始化的空间进行初始化形式:
new(指针)构造函数 //定位newStack* ptr = (Stack*)malloc(sizeof(Stack));//malloc 不会调用构造函数,此时未初始化new(ptr)Stack();//通过定位new初始化对象 6.1 应用场景
定位new 可以用在内存池这个项目中
定位new 来进行初始化 
