一.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;進行內存的釋放;若是在棧中,那麼在其做用域內有效,跳出做用域,內存自動釋放。
注意:
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