从c到c++:辨析new/delete、malloc/free、operator new
一、区别
从定义讲
- malloc/free 是操作符
- new/delete是函数
- 因此,前者的使用必须引用头文件
从流程讲
- malloc 申请了若干长度的内存,返回内存地址,仅此而已
- new 首先申请若干内存,继而调用构造函数,返回值带有类型不需要指定,此过程中出现问题会自动抛出异常
综上,new不仅更高效,也更安全。
附实例
ListNode* pList = (ListNode*)malloc(sizeof(ListNode));
对比
ListNode* pList = new ListNode;
当然,delete对应new主要比free多了析构函数,否则内存泄漏。
再往深处说,c++还有operator new ,实际上通常new的执行过程中的分配内存由operator new
完成,再结合异常检查,以及构造函数就构成了new。
对于operator new还有更多细节,随后补充,Clion下不方便看库。
二、new/delete、new[]/delete[]
仍以new为例,new我们经常
ListNode* pList = new ListNode;
但有时我们还会
int *p = new int[10]
我们可以申请一组空间给p,如果你忘了delete[] 而是delete,则程序会崩溃。
原因显而易见:程序对于一个“数组”,并不知道需要析构多少次。
那么就有一个有趣的问题,delete[] 又怎么知道需要析构多少次?
vs下调试步入源码可以看到,对delete与delete[]申请的空间的结构是不一样的
如上图,new[]会多申请一个整型空间于整片内存的最前面,用以存储“数组元素长度”
同时,delete[]不同于delete,其会读取实际数据所在空间前的空间,以判断需要执行多少次析构。
三、operator new 简述
operator new有三种形式:
throwing (1) void* operator new (std::size_t size) throw (std::bad_alloc); nothrow (2) void* operator new (std::size_t size, const std::nothrow_t& nothrow_value) >throw(); placement (3) void* operator new (std::size_t size, void* ptr) throw();
(1)(2)的区别仅是是否抛出异常,当分配失败时,前者会抛出bad_alloc异常,后者返回null,不会抛出异常。它们都分配一个固定大小的连续内存。
(3)是placement new,它也是对operator new的一个重载,定义于#include 中,它多接收一个ptr参数,但它只是简单地返回ptr。其在new.h下的源代码如下:
#ifndef __PLACEMENT_NEW_INLINE #define __PLACEMENT_NEW_INLINE inline void *__cdecl operator new(size_t, void *_P) {return (_P); } #if _MSC_VER >= 1200 inline void __cdecl operator delete(void *, void *) {return; } #endif #endif
那么它究竟有什么用呢?事实上,它可以实现在ptr所指地址上构建一个对象(通过调用其构造函数),这在内存池技术上有广泛应用。
它的调用形式为:
new(p) A(); //也可用A(5)等有参构造函数
使用宏模拟new[] 以回顾上述问题以及简单探究operator new
#define NEW_ARRAY(PTR,TYPE,N) \
do{ \ //定义do-while以避免宏出现优先级问题
PTR = (TYPR*)operator new(sizeof(TYPE)*N + 4); \ //申请若干空间并返回对应类型指针
(*(int*)PTR) = N; \ //存储对象个数,以方便随后析构
PTR = (TYPE*)((char*(PTR)+4)); \ //将指针向后移动,指向数据的开始
for (size_t i = 0; i < N; i++) \
new(PTR + i)TYPE; \ //对应operator new第三种形式,手动构造
} while (false);
使用宏模拟delete[] ,对应new[]代码
#define DELETE_ARRAY(PTR,TYPE) \
do{ \
size_t N = *((int*)PTR - 1); \
for (size_t i = 0; i < N; i++) \
PTR[i].~TYPE(); \
PTR = (TYPE*)((char*)PTR - 4); \
operator delete(PTR); \
} while (false);