C++ 动态内存分配
一、new/delete
1.1 为什么需要new/delete
C语言中通过malloc/free等函数实现动态内存管理,为什么C++中还引入new/delete对动态内存管理?原因在于:malloc/free等函数不能满足对自定义类型的对象管理要求,对象在创建时要自动调用构造函数,对象在销毁前要自动调用析构函数,由于malloc/free是库函数而不是运算符,编译器不能把调用构造函数和析构函数的任务强加给它们。因为C++需要一个能够完成动态内存分配和初始化工作的运算符new,以及一个能够完成资源释放和释放内存工作的运算符delete。
注意:new/delete是运算符,而不是函数
1.2 new/delete对内置类型处理
new/delete对内置类型处理与malloc/free对内置类型处理没有本质区别,只是用法不一样。new直接返回目标类型的指针,不需要显式类型转换,而malloc返回void*,必须显式地转换为目标类型后使用
用法:类型名* 指针变量名 = new 类型名; delete 指针变量名;类型名* 指针变量名 = new 类型名[元素个数]; delete[] 指针变量名;
1 |
|
malloc与new对内置类型都没有初始化,但可以int* p3 = new int(1); 将对应空间值初始化为1
1.3 new/delete对自定义类型处理
malloc/free对自定义类型只会开辟/销毁空间,并不会调用构造函数/析构函数。new先开辟空间然后调用构造函数初始化,delete先调用析构函数释放资源然后销毁空间
1 |
|
输出
1 | 调用构造函数 |
使用malloc为自定义类型分配空间需要手动调用InitSeq函数初始化,业务处理后需要调用DestorySeq函数进行资源释放然后free释放空间,比较麻烦且容易忘记初始化及释放资源。使用new/delete会自动调用构造函数/析构函数帮我们进行处理
二、new/delete 实现原理
new运算符底层是调用 operator new函数与构造函数实现的,delete运算符底层是调用析构函数与 operator delete函数实现的
2.1 operator new函数
operator new为库函数,源代码如下:
1 | void *__CRTDECL operator new(size_t size) _THROW1(_STD bad_alloc) |
可以发现operator new函数申请空间还是调用malloc函数实现的,申请成功返回地址,申请空间失败会抛出异常。可以发现之所以用operator new而不是直接用malloc主要是对于申请失败时抛出异常
2.2 operator delete函数
operator delete为库函数,源代码如下:
1 | void operator delete(void* pUserData) |
operator delete 通过调用free来释放空间的。
2.3 new/delete对内置类型实现原理
如果申请的是内置类型的空间,new和malloc,delete和free基本类似。只是new在申请空间失败时会抛异常,malloc会返回NULL
2.4 new/delete对自定义类型实现原理
new:
● 先调用operator new函数为对象动态内存分配空间
● 然后调用构造函数对对象初始化
delete:
● 先调用析构函数释放资源
● 然后调用operator delete函数释放对象的空间
new [N]:
● 先调用operator new[ ]函数对对象动态内存分配空间 (operator new[ ]函数内调用operator new函数实现对N个对象空间分配)
● 然后调用N次构造函数对对象初始化
delete[ ]:
● 先调用N次析构函数,对N个对象释放资源
● 然后调用operator delete[ ]函数释放空间(operator delete[ ]函数内调用operator delete函数实现对N个对象空间释放)
2.5 operator new与operator delete 函数重载
有些情况下希望动态内存空间不是来源于堆而是来源内存池,以提高效率。可以在类中对operator new与operator delete函数重载。类中对象申请/释放的是内存池空间,类外还是从堆中申请/释放
1 | class ListNode |
三、placement-new
placement-new:在一块已分配的内存空间上调用构造函数初始化对象或对象数组
用法:new (place_address) typenew (place_address) type(initializer-list)new (place_address) type[元素个数]
placement-new主要应用场景是配合内存池使用:使用一块较大的动态内存分配空间,用来构造不同类型的对象或对象数组。
注意:placement-new构造的对象或对象数组要显式调用它们的析构函数进行资源释放,不要使用delete。因为构造起来的对象或对象数组大小并不一定等于原来申请空间大小,使用delete会造成内存泄漏或在之后释放原来申请空间出现运行时错误
1 |
|
四、C/C++ 动态内存分配对比
C语言中使用malloc/free等函数进行动态内存分配,C++中使用new/delete运算符进行动态内存分配.
相同点:
● 都是从堆上分配空间,需要手动释放空间
不同点:
● malloc和free是函数,new和delete是运算符
● 对于自定义类型,malloc/free不会调用构造函数/析构函数,new会调用构造函数/析构函数
● malloc申请空间时,需要手动计算空间大小,new只需给类型名
● malloc的返回值为void*, 在使用时必须强转,new返回的是类型指针
● malloc申请空间失败返回NULL,new申请抛出异常