1. 程式人生 > >C++ Primer第五版筆記——過載new和delete

C++ Primer第五版筆記——過載new和delete

要想掌握過載new和delete的方法,首先要對new和delete表示式的工作機理有更多瞭解,當執行一條new/delete表示式時:

string* sp = new string("hello");
string* arr = new string[10];
delete sp;
delete[] arr;

對new來說實際執行了三個步驟:
第一步、new表示式呼叫一個operator new(或者 operator new[])的標準庫函式。該函式分配一塊足夠大的、未命名的、原始的記憶體空間以便於儲存特定型別的物件(或者物件陣列);
第二步、編譯器執行相應的建構函式以構造這些物件,併為其傳入初始值;
第三步、物件被分配了空間並構造完成,返回一個指向該物件的指標。
對delete來說實際執行了兩步操作:


第一步、對sp所指物件或者arr所指的物件陣列中的元素執行對應的析構操作;
第二步、編譯器呼叫operator delete(或者operator delete[])的標準庫函式釋放記憶體空間。
  應用程式可以在全域性區域中定義operator new和operator delete函式(這時程式就擔當起控制動態記憶體分配的職責,這兩函式必須是正確的),也可以將他們定義為成員函式。當編譯器發現一條new或delete表示式時,將在程式中尋找可供呼叫的operator函式,如果被分配的型別是類型別,則首先在該類及其基類中查詢,沒有找到則會在全域性作用域中查詢(也可以使用::運算子限制直接執行全域性區域中的函式),仍然沒有則會使用標準庫定義的函式。


operator new 介面和operator delete 介面
  標準庫定義了operator new函式和operator delete函式的8個過載版本。其中前4個版本可能丟擲bad_alloc異常,後4個版本則不會丟擲異常:

//這些版本可能丟擲異常
void* operator new(size_t);							//分配一個物件
void* operator new[](size_t);						//分配一個數組
void* operator delete(void*) noexcept;		//釋放一個物件
void* operator delete[](void*) noexcept;		//釋放一個數組

//這些版本不會丟擲異常
void* operator new(size_t,nothrow_t&) noexcept;
void* operator new[](size_t,nothrow_t&) noexcept;
void* operator delete(void*,nothrow_t&) noexcept;
void* operator delete[](void*,nothrow_t&) noexcept;

對於operator new和operator new[]函式來說,它們的返回值必須是void*,第一個形參的型別必須是size_t且該形參不能含有預設實參。當編譯器呼叫前者時,把儲存指定型別物件所需的位元組數傳給size_t形參;當編譯器呼叫後者時,傳入函式的則是儲存陣列中所有元素所需的空間。儘管在一般情況下我們可以自定義具有任何形參的operator new,但是以下版本不能被過載:

void* operator new(size_t,void*);

對於operator delete和operator delete[]函式來說,它們的返回型別必須是void,第一個形參型別必須是void*。執行一條delete表示式將呼叫相應的operator函式,並用指向待釋放記憶體的指標來初始化void*形參。
  當將這兩個函式定義為類的成員時,該函式可以包含另一個size_t的形參,此時,該形參的初始值是第一個形參指向的物件的位元組數。size_t形參可用於刪除繼承體系中的物件。如果基類有一個虛解構函式,則傳遞給operator delete的位元組數將因待刪除指標所指向的物件的動態型別的不同而有所區別。而且,實際執行的operator delete函式版本也由物件的動態型別決定。


malloc函式和free函式
  當定義了自己的全域性operator new或operator delete後,必須以某種方式執行分配記憶體與釋放記憶體的操作,為此可以使用malloc和free函式,c++從c語言中繼承這些函式,並將其定義在cstdlib標頭檔案中。
  malloc函式接受一個表示待分配位元組數的size_t,返回指向分配空間的指標或者返回0以表示分配失敗。free接受一個void*,它是malloc返回的指標的副本,free將相關記憶體返回給系統(呼叫free(0)沒有意義)。


例子:

void* operator new(size_t size){
	if(void* mem = malloc(size)){
		return mem;
	}
	else{
		throw bad_malloc();	
	}
}

void operator delete(void* mem) noexcept {
	free(mem);
}