c++ new operator和operator new,delete operator和operator delete

1 new operator 和 operator new,delete operator 和 operator delete

new operator: c++中的關鍵字new,如A *a = new A;

operator new:c++中的一個操作符,並且可以被重載(類似加減乘除操作符)

operator new can be called explicitly as a regular function, but in C++, new is an operator with a very specific behavior: An expression with the new operator, first calls function operator new (i.e., this function) with the size of its type specifier as first argument, and if this is successful, it then automatically initializes or constructs the object (if needed). Finally, the expression evaluates as a pointer to the appropriate type.

operator new和operator delete的聲明

void *operator new(size_t);     //allocate an object
void *operator delete(void *);    //free an object
 
void *operator new[](size_t);     //allocate an array
void *operator delete[](void *);    //free an array

例如:

A* a = new A;

分三步:

1 分配內存;2 調用A()構造對象;3 返回分配指針

事實上,分配內存這一操作就是由operator new(size_t)來完成的,如果類A重載了operator new,那麼將調用A::operator new(size_t ),否則調用全局::operator new(size_t ),後者由C++默認提供。因此前面的步驟也就是:

1 調用operator new (sizeof(A)); 2 調用A::A(); 3 返回指針

下面的圖是一個具體的示例

簡單總結下:

  • 首先需要調用上面提到的 operator new 標準庫函數,傳入的參數爲 class A 的大小,這裏爲 8 個字節,至於爲什麼是 8 個字節,你可以看看《深入 C++ 對象模型》一書,這裏不做多解釋。這樣函數返回的是分配內存的起始地址,這裏假設是 0x007da290。
  • 上面分配的內存是未初始化的,也是未類型化的,第二步就在這一塊原始的內存上對類對象進行初始化,調用的是相應的構造函數,這裏是調用 A:A(10); 這個函數,從圖中也可以看到對這塊申請的內存進行了初始化,var=10, file 指向打開的文件。
  • 最後一步就是返回新分配並構造好的對象的指針,這裏 pA 就指向 0x007da290 這塊內存,pA 的類型爲類 A 對象的指針。

delete pA 的過程:

delete 就做了兩件事情:

  • 調用 pA 指向對象的析構函數,對打開的文件進行關閉。
  • 通過上面提到的標準庫函數 operator delete 來釋放該對象的內存,傳入函數的參數爲 pA 的值,也就是 0x007d290。

 

2 operator new的三種形式

throwing (1)    
void* operator new (std::size_t size) throw (std::bad_alloc);
nothrow (2) 
void* operator new (std::size_t size, const std::nothrow_t& nothrow_value) throw();
placement (3)   
void* operator new (std::size_t size, void* ptr) throw();

(1)(2)的區別僅是是否拋出異常,當分配失敗時,前者會拋出bad_alloc異常,後者返回null,不會拋出異常。它們都分配一個固定大小的連續內存。

A* a = new A; //調用throwing(1)
A* a = new(std::nothrow) A; //調用nothrow(2)

3 new[] 和 delete[]

假設 class A *pAa = new A[3];

從這個圖中我們可以看到申請時在數組對象的上面還多分配了 4 個字節用來保存數組的大小,但是最終返回的是對象數組的指針,而不是所有分配空間的起始地址。

這樣的話,釋放就很簡單了:

delete []pAa;

這裏要注意的兩點是:

  • 調用析構函數的次數是從數組對象指針前面的 4 個字節中取出;
  • 傳入 operator delete[] 函數的參數不是數組對象的指針 pAa,而是 pAa 的值減 4。

4 爲什麼 new/delete 、new []/delete[] 要配對使用?

4.1 使用new[]分配內存,delete釋放

內置類型:


int *pia = new int[10];
delete []pia;

內置類型因爲不需要調用析構函數,所以使用new[]分配的內存的時候沒有多分配4個字節保存數組大小;所以此時使用delete 釋放內存不會有問題

帶有自定義析構函數的類類型:

class A *pAa = new class A[3];
delete pAa;

問題有兩點,第一點是隻對第一個對象調用了析構函數,剩下兩個對象沒有調用析構函數,如果類對象中申請了大量的內存需要在析構函數中釋放,而你卻在銷燬數組對象時少調用了析構函數,這會造成內存泄漏;

第二點是致命的,直接釋放 pAa 指向的內存空間,這個總是會造成嚴重的段錯誤,程序必然會崩潰!因爲分配的空間的起始地址是 pAa 指向的地方減去 4 個字節的地方。你應該將傳入參數設爲那個地址!

4.2 使用new分配內存,delete[]釋放

同4.1進行分析

總的來說,記住一點即可:new/delete、new[]/delete[] 要配套使用總是沒錯的!

5 動態創建對象時的初始化問題

int *a = new int(5)  //allocates an integer, set to 5. (same syntax as constructors)

int *a = new int     //allocates an integer,明確不初始化,內置類型的對象無初始化

int *a = new int()  //allocates an integer,採用值初始化,將*a初始化爲0

 string *ps = new string(10,'9')    //使用給定的值初始化該內存空間(十個9的字符串)

string *ps = new string                //明確不初始化,但是string會使用默認構造函數初始化(initialized to empty string)

string *ps = new string()             //對於提供了默認構造函數的類類型,無論程序是明確的不初始化還是要求進行值初始化,都會自動調用默認構造函數初始化該對象

int *a = new int[5];

int *a = new int[5]();

原理同上(沒有 int *a = new int[5](9) 這種寫法)

 

 

參考文章:

https://blog.csdn.net/WUDAIJUN/article/details/9273339

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章