C++ new操做符詳解

一.new操做符的概念html

咱們一般講的new是指的是new operator,其實還有另外兩個概念,operator new 和 placement new。編程

一、new operator        數組

咱們在使用new operator的時候,其實是執行了三個步驟:函數

1)調用operator new分配內存 ;2)調用構造函數生成類對象;3)返回相應指針。spa

二、operator new.net

因此說operator new作的事情是new operator的一部分。指針

operator new的原型是code

Void* operator new(size_t size);htm

參數size指定待分配的內存大小,函數內部調用malloc初始化內存,返回指向這個內存的指針。對象

你能夠重載這個函數(注意是重載operator new,而不能重載new operator)。operator new默認狀況下首先調用分配內存的代碼,嘗試獲得一段堆上的空間,若是成功就返回,若是失敗,則轉而去調用一個new_hander(異常處理函數),若沒有定義new_hander,則拋出異常,不然執行new_hander,而後繼續重複前面過程。你能夠在重載的時候加上額外的參數,可是第一個參數類型必須是size_t.例如:

class A
{
public:
   void* operator new(size_t size)
   {
       printf("operator new calledn");
       return ::operator new(size);
   }
};
A* a = new A();

這裏經過::operator new調用了原有的全局的new,實現了在分配內存以前輸出一句話。全局的operator new也是能夠重載的,但這樣一來就不能再遞歸的使用new來分配內存,而只能使用malloc了:

void* operator new(size_t size)
{
   printf("global newn");
   return malloc(size);
}

相應的,delete也有delete operator和operator delete之分,後者也是能夠重載的。而且,若是重載了operator new,就應該也相應的重載operator delete,這是良好的編程習慣。

 

三、placement new

placement new是用來實現定位構造的,所以能夠實現new operator三步操做中的第二步。

其實它也只是operator new的一個重載的版本,只是咱們不多用到它。若是你想在已經分配的內存中建立一個對象,使用new時行不通的。也就是說placement new容許你在一個已經分配好的內存中(棧或者堆中)構造一個新的對象。原型中void*p實際上就是指向一個已經分配好的內存緩衝區的的首地址。

 

我 們知道使用new操做符分配內存須要在堆中查找足夠大的剩餘空間,這個操做速度是很慢的,並且有可能出現沒法分配內存的異常(空間不夠)。 placement new就能夠解決這個問題。咱們構造對象都是在一個預先準備好了的內存緩衝區中進行,不須要查找內存,內存分配的時間是常數;並且不會出如今程序運行中途 出現內存不足的異常。因此,placement new很是適合那些對時間要求比較高,長時間運行不但願被打斷的應用程序。

使用方法以下:
1. 緩衝區提早分配
可使用堆的空間,也可使用棧的空間,因此分配方式有以下兩種:

class MyClass {…};
char *buf=new char[N*sizeof(MyClass)+sizeof(int)];或者char buf[N*sizeof(MyClass)+sizeof(int)];

2. 對象的構造

MyClass * pClass=new(buf) MyClass;

3. 對象的銷燬
一旦這個對象使用完畢,你必須顯式的調用類的析構函數進行銷燬對象。但此時內存空間不會被釋放,以便其餘的對象的構造。

pClass->~MyClass();

4. 內存的釋放
若是緩衝區在堆中,那麼調用delete[] buf;進行內存的釋放;若是在棧中,那麼在其做用域內有效,跳出做用域,內存自動釋放。

注意:

    • 在C++標準中,對於placement operator new []有以下的說明: placement operator new[] needs implementation-defined amount of additional storage to save a size of array. 因此咱們必須申請比原始對象大小多出sizeof(int)個字節來存放對象的個數,或者說數組的大小。
    • 使用方法第二步中的new纔是placement new,實際上是沒有申請內存的,只是調用了構造函數,返回一個指向已經分配好的內存的一個指針,因此對象銷燬的時候不須要調用delete釋放空間,但必須調用析構函數銷燬對象

placement new 是重載operator new 的一個標準、全局的版本,它不可以被自定義的版本代替(不像普通版本的operator new 和 operator delete可以被替換)。
void *operator new( size_t, void *p ) throw()     { return p; }
placement new的執行忽略了size_t參數,只使用第二個參數。其結果是容許用戶把一個對象放到一個特定的地方,達到調用構造函數的效果。
和其餘普通的new不一樣的是,它在括號裏多了另一個參數。好比:
Widget * p = new Widget; - - - - - - - - - //ordinary new 
pi = new (ptr) int; pi = new (ptr) int;     //placement new

括號裏的參數ptr是一個指針,它指向一個內存緩衝器,placement new將在這個緩衝器上分配一個對象。Placement new的返回值是這個被構造對象的地址(好比括號中的傳遞參數)。placement new主要適用於:在對時間要求很是高的應用程序中,由於這些程序分配的時間是肯定的;長時間運行而不被打斷的程序;

 

3、處理內存分配異常

正如前面所說,operator new的默認行爲是請求分配內存,若是成功就返回,若是失敗,則轉而去調用一個new_hander(異常處理函數),若沒有定義new_hander,則拋出異常,不然執行new_hander,而後繼續重複前面過程。因而,想要從operator new的執行過程當中返回,則必然須要知足下列條件之一:

1)分配內存成功

2)new_handler中拋出bad_alloc異常

3)new_handler中調用exit()或相似的函數,使程序結束

因而,咱們能夠假設默認狀況下operator new的行爲是這樣的:

    void* operator new(size_t size)  
    {  
       void* p = null  
       while(!(p = malloc(size)))  
       {  
           if(null == new_handler)  
              throw bad_alloc();  
           try  
           {  
              new_handler();  
           }  
           catch(bad_alloc e)  
           {  
              throw e;  
           }  
           catch(…)  
           {}  
       }  
       return p;  
    }  

當operator new不能知足一個內存分配請求的時候,默認會拋出一個異常,咱們能夠經過設置new_handler定義處置策略。

new_handler的模型爲:void (*new_handler)()

能夠經過「void set_new_handler( void(*new_handler)()) throw();」設置這個處理函數(new_handler),它定義在<new>標準函數庫中:

namespace std
{
    void (*new_handler)();
    void set_new_handler( new_handler )throw();
}
//error-handling function
void MemErrorHandling()
{
    std::cerr << "Failed to allocate memory.\n";
    std::abort();
}
... ...
std::set_new_handler(MemErrorHandling);

如今咱們知道了new操做失敗後,系統地大概處理流程,以及怎麼設置用戶自定義處理函數,可是咱們究竟能夠在new_handler中作些什麼處理呢?

一、刪除其它無用的內存,使系統具備能夠更多的內存可使用,爲下一步的內存申請做準備。
二、設置另一個new_handler。若是當前的new_handler不可以作到更多的內存申請操做,或者它知道另一個new_handler可 以作到,則能夠調用set_new_handler函數設置另一個new_handler,這樣在operator new下一次調用的時候,可使用這個新的new_handler。
三、卸載new_handler(經過set_new_handler(0)),使operator new在下一次調用的時候,由於new_handler爲空拋出內存申請異常。
四、拋出自定義異常。
五、再也不返回,調用abort或者exit退出程序。

 

參考:
一、http://www.bc-cn.net/Article/kfyy/cjj/jszl/200604/4002.html

二、http://blog.csdn.net/youdianmengxiangba/article/details/8233651

相關文章
相關標籤/搜索