内存管理
1.malloc1
2.new delete关键字2
3.operator new和operator3
4.定位new4
5.malloc free和new delete异同5
malloc calloc realloc free函数
内存区域划分
在C语言中常用malloc calloc realloc free开辟和释放位于堆上的动态内存。
malloc:
void* malloc (size_t size);
calloc:
void* calloc (size_t num, size_t size);
realloc:
void* realloc (void* ptr, size_t size);
free:
void free (void* ptr);
malloc calloc realloc函数都需要手动检查返回值确认空间是否开辟成功。 ↩︎new delete关键字
在C++中一般使用new delete关键字代替malloc calloc realloc free函数。
new和delete语法
new和delete,new[]和delete[]一定要配套,否则可能引起编译错误。new[]申请的是连续的空间。
对于内置类型来说,new/delte和malloc/free组合使用起来没有太大区别,只是省去了强制类型转换,让代码更加直观。
对于自定义类型来说,new/delte比malloc/free要方便很多,new可以在开辟空间后自动调用构造函数,delete可以在调用析构函数后清理对象的空间。
new的实现原理
1.call operator new()函数
2.call stack::stack调用stack类的构造函数
delete的实现原理
1.call stack::~stack调用stack类的析构函数
2.call operator delete()函数
方括号实现原理
new T[N]的原理
-
调用operator new[]函数,在operator new[]中实际调用operator new函数完成N个对象空间的申
请 -
在申请的空间上执行N次构造函数
delete[]的原理
-
在释放的对象空间上执行N次析构函数,完成N个对象中资源的清理
-
调用operator delete[]释放空间,实际在operator delete[]中调用operator delete来释放空间
-
operator new和operator delete函数
new和delete是用户进行动态内存申请和释放的操作符,operator new 和operator delete是系统提供的全局函数,new在底层调用operator new全局函数来申请空间,delete在底层通过operator delete全局函数来释放空间
operator new()
void *__CRTDECL operator new(size_t size) _THROW1(_STD bad_alloc) { // try to allocate size bytes void *p; while ((p = malloc(size)) == 0) if (_callnewh(size) == 0) { // report no memory // 如果申请内存失败了,这里会抛出bad_alloc类型异常 static const std::bad_alloc nomem; _RAISE(nomem); } return (p); }
operator new函数首先通过malloc函数开辟空间,假如开辟失败会抛出bad_alloc 类型异常,可以在函数外用try/catch捕获异常。
operator delete()
void operator delete(void *pUserData) { _CrtMemBlockHeader * pHead; RTCCALLBACK(_RTC_Free_hook, (pUserData, 0)); if (pUserData == NULL) return; 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; }
operator delete 最终也是通过free来释放空间的
operator new和delete的类专属重载
当该类局部和全局都有operator new和delete时,对象优先调用自己类里面的operator重载函数。
下面代码演示了,针对链表的节点ListNode通过重载类专属 operator new/ operator delete,实现链表节点使用内存池申请和释放内存,提高效率。
↩︎struct ListNode { ListNode* _next; ListNode* _prev; int _data; void* operator new(size_t n) { void* p = nullptr; p = allocator<ListNode>().allocate(1); cout << "memory pool allocate" << endl; return p; } void operator delete(void* p) { allocator<ListNode>().deallocate((ListNode*)p, 1); cout << "memory pool deallocate" << endl; } }; class List { public: List() { _head = new ListNode; _head->_next = _head; _head->_prev = _head; } ~List() { ListNode* cur = _head->_next; while (cur != _head) { ListNode* next = cur->_next; delete cur; cur = next; } delete _head; _head = nullptr; } private: ListNode* _head; }; int main() { List l; return 0; }
定位new
使用格式:
new (place_address) type或者new (place_address) type(initializer-list)
定位new表达式在实际中一般是配合内存池使用。因为内存池分配出的内存没有初始化,所以如果是自定义类型的对象,需要使用new的定义表达式进行显示调构造函数进行初始化。
调用构造函数的三种方法
1.成员初始化
2.使用new关键字
3.使用定位new(可以令已经初始化的成员再次调用构造函数) ↩︎
malloc free和new delete异同
malloc/free和new/delete的共同点是:
都是从堆上申请空间,并且需要用户手动释放。
不同的地方是:
- malloc和free是函数,new和delete是操作符
- malloc申请的空间不会初始化,new可以初始化
- malloc申请空间时,需要手动计算空间大小并传递,new只需在其后跟上空间的类型即可
- malloc的返回值为void*, 在使用时必须强转,new不需要,因为new后跟的是空间的类型
- malloc申请空间失败时,返回的是NULL,因此使用时必须判空,new不需要,但是new需要捕获异常
- 申请自定义类型对象时,malloc/free只会开辟空间,不会调用构造函数与析构函数,而new在申请空间
后会调用构造函数完成对象的初始化,delete在释放空间前会调用析构函数完成空间中资源的清理