8 理解不同含義的new和delete
C++使用new
操作符(new operator
/ new
)來完成動態記憶體的分配,其的實現分為兩步:通過operator new
分配足夠的堆記憶體,呼叫建構函式初始化記憶體物件。類似的,delete
首先呼叫解構函式,然後通過operator delete
釋放記憶體。
new、delete操作符和sizeof
、typeid
一樣是語言內建的關鍵字,不能過載以改變它的含義,但可以過載new操作
(operator new
)、delete操作
來改變記憶體的分配方式。必須要明確,這些操作符所完成的功能被明確固定,定製函式絕不能改變它們完成的功能,只能改變實現功能的方法。
operator new
new
operator new
完成必要的記憶體分配,其的函式形式為:void * operator new(size_t size);
。operator new
返回一塊未經處理的堆記憶體的首地址,引數size
決定的記憶體塊的大小。可見,其和malloc
非常接近。
編譯器提供了6種operator new
的全域性過載形式,並允許使用者提供自定義的operator new
,引數可以和已有形式完全相同(但不能在已有引數形式上增加含預設值的引數,避免引起歧義)。
void *operator new(std::size_t count) throw(std::bad_alloc);//標準版本 void *operator new(std::size_t count, const std::nothrow_t&) throw(); //相容早版本的new,不會丟擲異常 void *operator new(std::size_t count, void *ptr) throw();//placement new void *operator new[](std::size_t count) throw(std::bad_alloc);//new[]版本 void *operator new[](std::size_t count, const std::nothrow_t&) throw(); void *operator new[](std::size_t count, void *ptr) throw();
在呼叫new
時提供對應的引數以呼叫合適的operator new
。
void * memory = operator new(sizeof(Awesome));
Awesome * pObj = new (memory) Awesome;//呼叫placement new
過載operator new
過載operator new
函式的返回值必須是void *
,可以新增新的形參,但第一個引數必須是size_t
型別。全域性過載函式的引數不需要自定義型別,引數可以和已有的完全一致。
void * operator new(size_t size);//new [type] void * operator new(size_t size, int arg);//new(int) [type] void * operator new(size_t size, int arg = 0);//error: 歧義 class Awesome { public: void * operator new(size_t size)//new Awesome { cout << "Awesome::operator new" << endl; return malloc(size); } };
通過過載可以自定義記憶體分配的過程,並通過提供合適的引數讓new
自動呼叫。當我們設計的類需要更細緻的記憶體管理時,就應該考慮過載類的operator new
、operator delete
placement new
placement new
是一個特殊的operator new
,用於可以在一個未經處理的記憶體上構造物件。
void *operator new(std::size_t count, void *ptr) throw();//placement new
void * memory = operator new(sizeof(Awesome));
Awesome * pObj = new (memory) Awesome;
引數ptr
執行未經處理的記憶體,作為new
的額外引數。
operator delete
operator delete
與new
的關係類似於operator new
與new
的關係。delete
會呼叫解構函式析構物件,然後釋放物件佔有的記憶體。換言之,解構函式本身不會釋放記憶體,必須藉由operator delete
完成。
delete pObj;
//相當於
pObj->~Awesome();
operator delete(pObj);
直接呼叫operator delete
可以釋放物件的記憶體,但物件關聯的其它記憶體就不會被釋放,必須在呼叫operator delete
前呼叫解構函式。
void operator delete(void *);
void operator delete[](void *);
暴露的operator delete
只有兩個版本。可以過載operator delete
。
placement delete
placement delete
並不能直接呼叫,編譯器只會在placement new
構造失敗時呼叫placement delete
清理記憶體。
手動釋放placement new
生成的物件時應顯式呼叫物件的解構函式,並在需要時在析構之後把記憶體釋放掉。
參考:
https://developer.aliyun.com/article/640510
https://www.zhihu.com/question/22947192