C++深入讲解内存管理

C++深入讲解内存管理 C/C内存分布首先我们来看一看以下代码中变量在内存中的存储位置。c/c内存分配图1.栈又叫做堆栈存储非静态局部变量/函数参数/返回值等等栈是向下增长的。2.内存映射段是高效的I/O映射方式用于装载一个共享的动态内存库。用户可使用系统接口创建共享内存做进程间通信。3.堆用于程序运行时动态内存分配堆上可以向上增长的。4.数据段 - 用于存储全局数据和静态数据。5.代码段 - 可执行的代码/只读常量。C语言中的动态内存管理malloc/calloc/realloc/free12345678910intmain(){int* p1 (int*)malloc(sizeof(int));int* p2 (int*)calloc(4,sizeof(int));int* p3 (int*)realloc(p2,sizeof(int) * 10);free(p1);free(p2);free(p3);return0;}malloc/calloc/realloc的区别malloc - 堆上动态开辟空间realloc - 堆上动态开辟空间 初始化为0 相当于malloc memsetcalloc - 针对已经有的空间进行扩容 原地扩容或异地扩容C的内存管理C语言的内存管理方式在c中可以继续使用但是有些地方使用起来就比较麻烦了。因此c提供了自己的内存管理方式通过new和delete操作符进行动态内存管理。 new和delete是运算符不是函数因此执行效率高。new和delete操作内置类型:1234567891011121314intmain(){// new/delete和malloc/free 针对内置类型没有任何差别只是用法不同//动态申请一个int类型的空间int* p1 newint;deletep1;//动态申请一个int类型的空间并初始化为10int* p2 newint(10);deletep2;//动态申请10个int类型的空间int* p3 newint[10];delete[] p3;return0;}注意:申请和释放单个元素的空间使用new和delete操作符申请和释放连续的空间使用new[]和delete[]。new和delete操作自定义类型:注意 在申请自定义类型的空间时new会调用构造函数delete会调用析构函数而malloc和free不会。对于以上总结一下1.c中如果是申请内置类型对象或者数组malloc和new没有什么区别。2.如果是自定义类型那么区别很大new和delete是开空间 初始化析构清理 释放空间malloc和free仅仅是开空间 释放空间。3.建议在c中无论是自定义类型还是内置类型的申请和释放尽量使用new和delete。operator new与operator delete函数new和delete是用户进行动态内存申请和释放的操作符operator new和 operator delete是系统提供的全局函数new在底层调用operator new 全局函数来申请空间delete在底层提供operator delete全局函数来释放空间。如下是c官方对于这两个函数的描述operator new和operator delete的实现代码12345678910111213141516171819202122232425262728293031323334353637383940414243/*operator new该函数实际通过malloc来申请空间当malloc申请空间成功时直接返回申请空间失败尝试执行空间不足应对措施如果改应对措施用户设置了则继续申请否则抛异常。*/void* __CRTDECL operatornew(size_tsize) _THROW1(_STD bad_alloc){// try to allocate size bytesvoid* p;while((p malloc(size)) 0)if(_callnewh(size) 0){// report no memory// 如果申请内存失败了这里会抛出bad_alloc 类型异常staticconststd::bad_alloc nomem;_RAISE(nomem);}return(p);}/*operator delete: 该函数最终是通过free来释放空间的*/voidoperatordelete(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 new实际上是通过malloc来申请空间的如果malloc申请空间成功就直接返回否则执行用户提供的空间不足的应对,如果用户提供该措施就继续申请否则就抛异常。operator delete最终是通过free来释放空间的。operator new的使用案例12345678910111213141516171819202122232425262728293031323334353637structListNode{ListNode(intdata 0):_next(nullptr),_prev(nullptr),_data(data){}ListNode* _next;ListNode* _prev;int_data;};//operator new的用法跟malloc和free是一样的都是在堆上申请空间//只是申请空间失败后的处理方式不一样malloc失败返回NULL,operator new失败以后抛异常intmain(){//C语言ListNode* p1 (ListNode*)malloc(sizeof(ListNode));free(p1);//cListNode* p2 (ListNode*)operatornew(sizeof(ListNode));operatordelete(p2);int* p3 (int*)malloc(100000000000000000);if(p3 NULL){cout malloc fail endl;}try{int* p4 (int*)operatornew(100000000000000000);}//开辟空间失败捕获异常信息catch(exception e){cout e.what() endl;}return0;}operator delete的使用案例12345678910111213141516171819202122232425classA{public:A(inta 0){cout A()this endl;}~A(){cout ~A()this endl;}private:int_a;};intmain(){//c语言 - A* p (A*)malloc(sizeof(A));//等价于直接用 A* p new A;A* p (A*)operatornew(sizeof(A));new(p)A;// new(p)A(2); 定位newplacement-new,显示调用构造函数初始化这块空间对象//等价于 delete pp-~A();//析构函数可以显示调用operatordelete(p);return0;}operator new与operator delete的类专属重载内存池内存池的主要作用是提高效率。通过一次性申请比较大的空间来避免小空间内存的频繁申请和释放每次需要为对象分配内存空间时在已经申请好的大的空间内分配。空闲区被按照对象大小划分为若干块每个块之间通过链表连接起来。☑️以下代码演示了针对链表的节点ListNode通过重载类专属 operator new / operator delete 实现链表节点使用内存池申请和释放内存提高效率。1234567891011121314151617181920212223242526272829303132333435363738394041424344454647structListNode{void* operatornew(size_tn){void* p nullptr;p allocatorListNode().allocate(1);cout memory pool allocate endl;returnp;}voidoperatordelete(void* p){allocatorListNode().deallocate((ListNode*)p, 1);cout memory pool deallocate endl;}ListNode* _next;ListNode* _prev;int_data;};classList{public:List(){_head newListNode();_head-_next _head;_head-_prev _head;}~List(){ListNode* cur _head-_next;while(cur ! _head){ListNode* next cur-_next;deletecur;cur next;}delete_head;_head nullptr;}private:ListNode* _head;};intmain(){List h;return0;}new和delete的实现原理内置类型若申请的是内置类型的空间new和mallocdelete和free基本相似。new/delete 申请和释放的是单个元素的空间new[]和delete[]申请的是连续空间。new在空间申请失败时会抛异常malloc申请空间失败则返回NULL。自定义类型new:调用operator new函数申请空间。在申请的空间调用构造函数完成对象的构造。delete:在空间上调用析构函数完成对象中资源清理的工作。调用operator delete函数释放对象的空间。new arr[N]:调用operator new[]函数在operator new[]中实际调用operator new函数完成N个对象空间的申请。在申请的空间上执行N次构造函数。delete []:在释放的空间上执行N次析构函数完成N个对象中资源的清理。调用operator delete[]释放空间在operator delete[]中调用operator delete来释放空间。定位new表达式(placement-new)一般来说使用new申请空间时是从系统堆上分配空间。申请所得的空间位置是根据当时的内存使用的实际情况来决定。但在某些特殊情况下可能需要在已分配的特定内存创建对象这就是所说的定位new(placement - new)。✔️默认情况下如果new不能分配所需要的内存空间那么它会抛出一个类型为bad_alloc的异常。我们可以改变使用new的方式来阻止其抛出异常123//如果申请失败new会返回一个空指针NULLint* p1 newint;//如果分配空间失败new会抛出std::bad_allocint* p2 new(nothrow)int;//如果分配空间失败new返回一个空指针这种形式的new我们就称为定位new。定位new表达式允许我们向new传递额外的参数。如上的nothrow。将nothrow传给new即不能抛出异常。如果这种形式的new不能分配所需内存那么它会返回一个空指针。