个人主页:Jason_from_China-CSDN博客
所属栏目:C++系统性学习_Jason_from_China的博客-CSDN博客
所属栏目:C++知识点的补充_Jason_from_China的博客-CSDN博客
前言
关于operator new和operator delete我们需要明确一个概念,这两个都是一个函数,和malloc,free一样都是一个函数,但是这里需要明确的是,这里只是类似,不是一样。
operator new语法结构
语法结构
#include<iostream> int main() {//标准的分配内存的空间形式//分配一个内置类型,int内置类型的空间void* ptr1 = operator new(sizeof(int));//分配一个数组形式的内存空间void* ptr4 = operator new[](10 * sizeof(int));//这样的形式也是可以使用的,但是可能会出现问题,因为operator new是一个没有初始化,也就是未定义的内存空间//这样分配内存容易导致错误访问,所以还是建议使用标准化来分配内存空间//void* ptr2 = operator new(10 * sizeof(int));//void* ptr3 = operator new[](sizeof(int));//void* ptr4 = operator new[](10 * sizeof(int));return 0; }
operator delete语法结构
#include<iostream> int main() {//标准的分配内存的空间形式//分配一个内置类型,int内置类型的空间void* ptr1 = operator new(sizeof(int));//分配一个数组形式的内存空间void* ptr4 = operator new[](10 * sizeof(int));//这样的形式也是可以使用的,但是可能会出现问题,因为operator new是一个没有初始化,也就是未定义的内存空间//这样分配内存容易导致错误访问,所以还是建议使用标准化来分配内存空间//void* ptr2 = operator new(10 * sizeof(int));//void* ptr3 = operator new[](sizeof(int));//void* ptr4 = operator new[](10 * sizeof(int));//operator new和delete这里是函数,所欲我们销毁的时候是函数的形式销毁,销毁的语法结构//释放单个内存空间//释放内存空间、标准化释放内存空间operator delete(ptr1);operator delete[](ptr4);return 0; }
operator new+operator delete原理讲解
关于operator new
- operator new我们可以看出来,其实new是operator new的一个封装,因为new在使用的时候会调用operator new
- operator new的底层实现上面是调用malloc来实现开辟空间的
关于operator delete
- 从operator delete我们可以看出来,delect本质也是对operator delete函数的封装,再严谨的讲解就是,是对free的封装,free是对free_dbg(p,_NORMAL_BLOCK)的封装
- 所以我们可以更清晰的看出,operator delete是一个函数,不是关键字
- delete是关键字,不是函数
operator new+operator delete和new+delete的深入对比
一、内置类型
- 对于内置类型,new 和 malloc、delete 和 free 基本类似。不同在于:new/delete 申请和释放单个元素空间,new []/delete [] 申请和释放连续空间;new 申请空间失败会抛异常,malloc 失败返回 NULL。
- 抛异常(就是告诉你哪里有错,并且继续运行程序)
二、自定义类型 new 的原理
- 调用 operator new 函数申请空间,底层类似 malloc(malloc 不抛异常)。
- 在申请的空间上执行构造函数完成对象构造。
三、自定义类型 delete 的原理
- 在空间上执行析构函数清理对象资源,本质类似 free 的调用。
- 调用 operator delete 函数释放对象空间。
四、new T [N] 的原理
- 调用 operator new [] 函数,实际在其中调用 operator new 完成 N 个对象空间申请。
- 在申请的空间上执行 N 次构造函数。
五、delete [] 的原理
- 在释放的对象空间上执行 N 次析构函数,清理 N 个对象资源。
- 调用 operator delete [] 释放空间,实际在其中调用 operator delete。
operator new+operator delete和new+delete使用时候的注意事项
不要交错使用,很容易导致资源使用出现问题
- operator new只是开辟空间,不会进行初始化的
- operator delete是只是销毁空间,不会清理资源的
- new,在开辟空间的时候会初始化并且构建资源
- delete,销毁空间的时候会调用构造函数销毁资源
- 知道,尽量不要交错使用就可以
#include<iostream> using namespace std;class A { public:A(int capacity = 4, int size = 0):_Capacity(capacity), _size(size), _arr(nullptr){//创建空间_arr = new int[_Capacity];printf("A()");}~A() {//这里释放我们是需要匹配方括号,这里是释放数组形式的内容,自适应找到需要释放的内存delete[] _arr;_Capacity = 4;_size = 0;printf("~A()");}private:int* _arr;int _Capacity;int _size; };//operator new创建空间,new构造 //我们需要使用 operator delete先销毁空间,再使用delete销毁资源 //并且是不能直接使用delete来进行销毁空间的,因为我们创建的空间是未定义的,new构造之后我们是会申请资源甚至空间的 //如果直接用delect销毁不使用operator delete销毁就会导致空间没有销毁 //如果只是使用operator delete销毁空间,就会导致资源没有销毁 //并且此时还应该先试用delete来销毁资源 再销毁空间 int main() {A* p = new A[10];delete[] p;printf("\n");void* ptr1 = operator new[](10 * sizeof(A));//初始化,我们也可以进行定位new进行初始化,定位new里面我们会进行讲解for (int i = 0; i < 10; ++i) {new (static_cast<A*>(ptr1) + i) A(i * 10);}// 使用这些 A 对象// 销毁这些对象for (int i = 0; i < 10; ++i) {(static_cast<A*>(ptr1) + i)->~A();}operator delete[](ptr1);return 0; }
- operator new创建空间,new构造
- 我们需要使用 operator delete先销毁空间,再使用delete销毁资源,并且是不能直接使用delete来进行销毁空间的,因为我们创建的空间是未定义的,new构造之后我们是会申请资源甚至空间的
- 如果直接用delect销毁不使用operator delete销毁就会导致空间没有销毁
- 如果只是使用operator delete销毁空间,就会导致资源没有销毁
- 并且此时还应该先使用delete来销毁资源 再使用operator delete销毁空间
定位new表达式(placement-new) (了解)
定位new主要使用的区域在于内存池,所以这里作为了解进行学习
一、在特定内存位置构造对象:
- 可以在预先分配好的内存区域中创建对象,而不依赖于默认的内存分配机制。例如,使用
operator new
或其他方式分配了一块内存后,可以使用定位new
在这块内存上构造对象。- 语法形式为:
new (pointer) Type(args...)
,其中pointer
是指向已分配内存的指针,Type
是要构造的对象类型,args...
是构造函数的参数。- 当使用普通的
new
和delete
操作符时,delete
会自动调用对象的析构函数并释放内存。但是对于通过定位new
创建的对象,由于没有通过常规的内存分配机制,仅仅使用delete
或者operator delete
来释放内存不会自动调用析构函数。- 此时我们发现可以用operator new开辟空间,new构造没有那么麻烦了,我们可以直接定位,可以直接看下面代码,但是这里有问题的就是不支持显示构造,但是支持显示析构
#include<iostream> using namespace std; class A { public:A(int capacity = 4, int size = 0):_Capacity(capacity), _size(size), _arr(nullptr){//创建空间_arr = new int[_Capacity];printf("A()");}~A() {//这里释放我们是需要匹配方括号,这里是释放数组形式的内容,自适应找到需要释放的内存delete[] _arr;_Capacity = 4;_size = 0;printf("~A()");}private:int* _arr;int _Capacity;int _size; };int main() {//正常函数的调用A* p = new A[10];delete[] p;printf("\n");//定位的使用,operator new创建一个空间,new定位进行构造void* ptr1 = operator new[](10 * sizeof(A));new(ptr1) A[10];//定位不支持显示构造,必须有默认构造//delete[] ptr1;//定位是需要显示调用析构函数来进行释放资源的。直接使用delect自动释放资源是不能实现的for (int i = 0; i < 10; i++){//static_cast强制类型转化关键字//<A*>转化为A类型//(ptr1) + i)->~A();循环显示调用析构函数(static_cast<A*>(ptr1) + i)->~A();}operator delete[](ptr1);return 0; }
二、与内存池等技术结合使用:
- 在一些高性能场景下,为了避免频繁的内存分配和释放开销,可以使用内存池预先分配一大块内存,然后在需要创建对象时使用定位
new
在内存池中选取合适的位置构造对象。- 这样可以提高内存分配的效率,减少内存碎片的产生。
三、资源管理和对象生命周期控制:
- 通过定位
new
,可以更精细地控制对象的构造和析构时机,特别是在一些复杂的资源管理场景中。- 例如,可以在特定的资源初始化后,在与之相关的内存位置构造对象,确保资源和对象的生命周期紧密关联。
四、注意事项:
- 使用定位
new
构造的对象,在销毁时需要手动调用析构函数,而不能直接使用delete
来释放内存,因为delete
会尝试释放由默认内存分配机制分配的内存,而不是定位new
所使用的内存。- 例如:
new (ptr) Type(args...);
构造的对象,在销毁时应该使用ptr->~Type();
来调用析构函数。
malloc/free 和 new/delete 的区别