1. 前言
在C语言中,有四个内存管理函数:
malloc,calloc,realloc和free
但是它们的使用十分的不方便:
int* p=(int*)malloc(sizeof(int)*n);
代码量很大,并且有一个新的问题:
malloc函数不会初始化变量
当变量是自定义类型的时候
C语言的内存管理函数无法
自动调用变量的构造/析构函数!
于是C++祖师爷发话了:
C++要自己搞一个内存管理!
2. new
2.1 new的使用方法
new的使用方法:
开辟多个空间
int* p = new int[100];
这段代码可以这样理解:
开辟一个空间并初始化
class A { public: A(int a = 0) : _a(a) {} private: int _a; }; A* p = new A(10);
使用小括号()
这段代码初始化了一个对象成10
并且将地址赋值给指针变量p
开辟多个空间并初始化
A* p = new A[5]{1,2,3};
这段代码可以这样理解:
2.2 new的特性(对比malloc)
new的特性:
- 对于内置类型来说,new和malloc
并没有太大的区别 - 对于自定义类型来说,new会调用
自定义类型的构造函数,而malloc不会 - new可以初始化变量的内容
3. delete
delete对比C语言中的free
是用来使用同台开辟的空间的/1
3.1 delete的使用方法
delete的使用:
直接使用delete
int* p = new int(10); //使用指针p delete p;
当new堆区空间时只开辟了一份空间
释放空间时直接使用delete即可
使用delete[]
int* p = new int[10]{1,2,3,4}; //使用指针p delete[] p;
当new使用方括号[]开辟多个空间时
delete也要对应的加上[]来释放空间
否则会出错
3.2 delete的特性(对比free)
delete特性:
- 对于内置类型,delete和free没有区别
- 对于自定义类型,delete会调用
自定义类型的析构函数,而free不会 - delete面对不同的情况需要加上[ ]
然而free不用考虑
对于自定义类型来说
有可能类中有指针指向一块堆区
如果不调用析构函数释放这块空间
直接用free释放掉对象的空间
那么这块空间就会内存泄漏!
4. 全局函数operator new
不同于C语言的malloc和free函数
new和delete是两个操作符
那new是怎样开辟空间的呢?
new在底层调用了operator new
注:
operator new是全局函数
不是运算符重载
库中的operator new函数:
void *__CRTDECL operator new(size_t size) _THROW1(_STD bad_alloc) { void *p; while ((p = malloc(size)) == 0) if (_callnewh(size) == 0) { // 如果申请内存失败了,这里会抛出bad_alloc 类型异常 static const std::bad_alloc nomem; _RAISE(nomem); } return (p); }
可以发现,new的底层实际上是
使用了malloc来实现开辟空间
开辟空间成功,会返回指向此空间的指针
开辟空间失败,会抛异常
抛异常不是本节课的重点
它是C++一个全新的知识
在后面的章节会详细介绍
一般情况下,new申请空间都不会失败!
5. 全局函数operator delete
和new对应的delete的底层
也是调用了operator delete来
释放堆区开辟的空间
库中的operator delete函数:
#define free(p) _free_dbg(p, _NORMAL_BLOCK) void operator delete(void *pUserData) { _CrtMemBlockHeader * pHead; RTCCALLBACK(_RTC_Free_hook, (pUserData, 0)); if (pUserData == NULL) return; _mlock(_HEAP_LOCK); __TRY pHead = pHdr(pUserData); _ASSERTE(_BLOCK_TYPE_IS_VALID(pHead->nBlockUse)); _free_dbg( pUserData, pHead->nBlockUse ); __FINALLY _munlock(_HEAP_LOCK); __END_TRY_FINALLY return; }
看不懂没关系,只需要抓住重点:
delete里面调用了:
_free_dbg(p, _NORMAL_BLOCK)
然而此函数正是free函数定义的宏替换!
说明delete的底层实现实际上
是调用了C语言中的free函数
delete是使用free来释放空间的
6. new的实现原理
对于内置类型来说,new和
malloc的实现是一样的
对于自定义类型来说
new的原理步骤
- 调用operator new函数申请空间
- 在申请的空间上执行构造函数
完成对象的构造
new T[N]的原理步骤
- 调用operator new[]函数
完成N个对象空间的申请 - 在申请的空间上执行N次构造函数
7. delete的实现原理
对于内置类型来说,delete
和free的实现是一样的
对于自定义类型来说
delete的原理步骤
- 在空间上执行析构函数
完成对象中资源的清理工作 - 调用operator delete函数释放对象的空间
delete[]的原理步骤
- 在释放的对象空间上执行N次析构函数
完成N个对象中资源的清理 - 调用operator delete[]释放空间
8. 总结以及拓展
此章总结完,就可以去new一个对象了
并且C++和C语言中的内存管理区别
是面试中的常考点,和指针与引用的区别
俗称"面试山上的看门二虎"
下面就来总结一下:
- malloc和free是函数,new和delete是操作符
- malloc申请的空间不会初始化,new可以初始化
- malloc申请空间时,需要手动计算空间大小并传递,new只需在其后跟上空间的类型即可,如果是多个对象,[]中指定对象个数即可
- malloc的返回值为void, 在使用时必须强转,new不需要,因为new后跟的是空间的类型*
- malloc申请空间失败时,返回的是NULL,因此使用时必须判空,new不需要,但是new需要捕获异常
- 申请自定义类型对象时,malloc/free只会开辟空间,不会调用构造函数与析构函数,而new在申请空间后会调用构造函数完成对象的初始化,delete在释放空间前会调用析构函数完成空间中资源的清理
拓展:内存泄漏
内存泄漏指因为疏忽或错误造成程序未能释放已经不再使用的内存的情况。内存泄漏并不是指内存在物理上的消失,而是应用程序分配某段内存后,因为设计错误,失去了对该段内存的控制,因而造成了内存的浪费
更多关于内存泄漏的内容:
内存泄漏介绍
内存泄漏原因
内存泄漏检测方法
内存泄漏检测工具
可以参考这两篇文章:
🔎 下期预告:C++模板初阶讲解 🔍