1. 程式人生 > 其它 >8 理解不同含義的new和delete

8 理解不同含義的new和delete

C++使用new操作符(new operator / new)來完成動態記憶體的分配,其的實現分為兩步:通過operator new分配足夠的堆記憶體,呼叫建構函式初始化記憶體物件。類似的,delete首先呼叫解構函式,然後通過operator delete釋放記憶體。

new、delete操作符和sizeoftypeid一樣是語言內建的關鍵字,不能過載以改變它的含義,但可以過載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 newoperator 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 deletenew的關係類似於operator newnew的關係。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