本章開始討論內存分配的一些用法,C/C++內存分配採用new和delete。在new申請內存時,可能會遇到的一種狀況就是,內存不夠了,這時候會拋出out of memory的異常。有的時候,咱們但願可以調用本身定製的異常處理函數,這就是本條款要說的。函數
在聲明於<new>的一個標準程序庫中,有以下的接口:spa
1 namespace std 2 { 3 typedef void (*new_handler)(); 4 new_handler set_new_handler(new handler p) throw(); 5 }
注意這裏面typedef了一個函數指針new_handler,它指向一個函數,這個函數的返回值爲void,形參也是void。set_new_handler就是將new_handler指向具體的函數,在這個函數裏面處理out of memory異常(函數末尾的throw()表示它不拋出任務異常),若是這個new_handler爲空,那麼這個函數沒有執行,就會拋出out of memory異常。指針
1 void MyOutOfMemory() 2 { 3 cout << "Out of memory error!" << endl; 4 abort(); 5 } 6 7 int main() 8 { 9 set_new_handler(MyOutOfMemory); 10 int *verybigmemory = new int[0x1fffffff]; 11 delete verybigmemory; 12 }
這裏預先設定好new異常時調用的函數爲MyOutOfMemory,而後故意申請一個很大的內存,就會走到MyOutOfMemory中來了。code
好,咱們更進一步,如今想要在不一樣的類裏面定製不一樣的new_handler處理機制,一種想法是在類內部定義set_new_handler函數,將new_handler做爲私有的成員變量,具體的new_handler函數能夠由構造函數傳入,但編譯器要求set_new_handler是靜態的,因此經過構造函數傳入new_handler不被編譯器支持,只能將set_new_handler與operator new都寫成靜態的,同時定義一個靜態的new_handler變量,像下面這樣:對象
1 class Widget 2 { 3 private: 4 static new_handler CurrentHandler; 5 6 public: 7 void set_new_handler(new_handler h) throw() 8 { 9 CurrentHandler = h; 10 } 11 static void* operator new(size_t size) 12 { 13 Widget::set_new_handler(CurrentHandler); 14 return ::operator new(size); 15 } 16 }; 17 18 new_handler Widget::CurrentHandler = 0;
屬於類的靜態變量CurrentHandler用於保存當前環境下的new_handler函數,在operator_new中,先設置成當前的new異常處理函數,再去調用std的operator new,執行內存分配操做。但這裏就存在問題了,set_new_handler到下一次設置它爲止,一直都是生效的,咱們只想在處理這個類對象的分配時用自定義的new_handler函數,可是相似於new int,new char這些基本類型,仍是但願走默認的new_handler(就是null,就是什麼也不執行,如咱們指望,這樣會拋出異常)。blog
一種天然的想法,就是在調用operator new末尾處還原new_handler,這就須要保存以前的new_handler,爲此,咱們構造一個NewHandlerHolder類,像下面這樣:繼承
1 class NewHandlerHolder 2 { 3 private: 4 new_handler SavedHandler; 5 NewHandlerHolder(const NewHandlerHolder&); 6 NewHandlerHolder& operator= (const NewHandlerHolder&); 7 8 public: 9 explicit NewHandlerHolder(new_handler h) :SavedHandler(h){} 10 ~NewHandlerHolder() 11 { 12 set_new_handler(SavedHandler); 13 } 14 };
這裏有一個SavedHandler成員變量,它在NewHandlerHolder構造時肯定具體的指向(其實就是指向系統默認的new_handler函數(即null),將拷貝構造函數與賦值運算符重載設置爲private是爲了防止出現拷貝的行爲(在編譯期就能夠阻止),這點能夠參照以前的條款。還要特別注意這裏的析構函數,它調用了set_new_handler,將new異常的處理恢復成SavedHandler(其實就是null)。接口
這樣咱們從新整理一下Widget,以下:內存
1 class Widget 2 { 3 private: 4 static new_handler CurrentHandler; 5 6 7 public: 8 static new_handler set_new_handler(new_handler h) throw() 9 { 10 new_handler OldHandler = CurrentHandler; 11 CurrentHandler = h; 12 return OldHandler; 13 } 14 static void* operator new(size_t size) 15 { 16 NewHandlerHolder h(Widget::set_new_handler(CurrentHandler)); 17 return ::operator new(size); 18 } 19 }; 20 21 new_handler Widget::CurrentHandler = 0;
爲了返回系統默認的new_handler,咱們在set_new_handler處理完以後,進行了舊handler的返回,同時在operator new的調用中進行了NewHandlerHolder的包裝,這樣在return以後,h會自動調用析構函數,恢復成默認的new_handler。ci
到這一步,本條款的重要內容已經說完了,但爲了不重複勞動,即爲每個須要重定義new_handler的類都寫一份set_new_handler和operator new,書上在最後對之進行了封裝,其實就是將Widget專門做爲這兩個函數(set_new_handler和operator new)的類,而後將須要自行處理new_handler的類public繼承於Widget便可。
最後總結一下:
set_new_handler容許客戶指定一個函數,在內存分配沒法得到知足時被調用。