處理new分配記憶體失敗情況
轉自:http://www.51testing.com/html/70/n-827070.html
在C++語言中,我們經常會使用new給一個物件分配記憶體空間,而當記憶體不夠會出現記憶體不足的情況。C++提供了兩中報告方式:
1、丟擲bad_alloc異常來報告分配失敗;
2、返回空指標,而不會丟擲異常。
C++為什麼會採用這兩種方式呢?這主要是由於各大編譯器公司設計C++編譯器公司的結果,因為標準C++是提供了異常機制的。例如,VC++6.0中當new分配記憶體失敗時會返回空指標,而不會丟擲異常。而gcc的編譯器對於C++標準支援比較好,所以當new分配記憶體失敗時會丟擲異常。
究竟為什麼會出現這種情況呢?
首先,C++是在C語言的基礎之上發展而來,而且C++發明時是想盡可能的與C語言相容。而C語言是一種沒有異常機制的語言,所以C++應該會提供一種沒有異常機制的new分配記憶體失敗報告機制;(確實是如此,早期的C++還沒有加入異常機制)
其次在返回空指標的實現過程中,C++採用的是malloc/calloc 等分配記憶體的函式,該類函式不會丟擲異常,但是在分配記憶體失敗時會返回“空指標”。
最後,對於標準的C++,有著比較完善的異常處理機制,所以對於出現異常時,會丟擲響應的異常。對於new分配失敗時,系統會丟擲bad_alloc異常。
鑑於以上原因,我們在不同的編譯器需要new分配失敗時做不同的處理。例如:
情況1:
int* p = new int(5); if ( p == 0 ) // 檢查 p 是否空指標 return -1; ... |
情況2:
try { int* p = new int(5); // 其它程式碼 } catch ( const bad_alloc& e ) { return -1; } |
情況1和情況2的程式碼都是對於new失敗時的處理,而針對不同的編譯器,可以這種處理會完全失效。如果在gcc編譯器採用情況1,那麼if(p==0)完全是沒有意義的,因為不管new記憶體分配成功失敗與否,都不會出現p=0的情況。即,如果分配成功,p=0完全不可能;而分配失敗,new會丟擲異常跳過其後面的程式碼。而需要採用情況2的處理方式,即應該來捕捉異常。
同樣,如果在VC++6.0中採用情況2的程式碼,那麼new分配失敗時,完全不會丟擲異常,那麼捕捉異常也是徒勞的。
所以在new分配記憶體的異常處理時要特別小心,可以兩種方式聯合使用,來解決跨平臺跨編譯器的難題。
當然情況2中的異常處理程式碼是最簡單的處理方式,下面我們為其制定一個客戶制定的錯誤處理函式,即new-handler。
typedef void (*new_handler)(); new_handler set_new_handler(new_handler p) throw(); |
這裡首先定義new-handler函式指標型別,然後定義一個new-handler函式set_new_handler,其引數是指向operator new無法分配足夠記憶體時應該被呼叫的函式。其返回指也是一個指標,指向set_new_handler被呼叫前正在執行(但是馬上就要被替換)的那個new-handler函式。下面設計一個當operator new無法分配足夠記憶體時應該被呼叫的函式:
void noMemoryToAlloc() std::abort(); |
使用noMemoryToAlloc函式的程式碼為:
int main() ... } |
當operator new無法分配足夠空間時,noMemoryToAlloc就會被呼叫,於是程式就會發出一個錯誤資訊cerr之後,呼叫abort函式結束程式。
如果operator new無法分配足夠空間時,我們希望不斷呼叫new-handler函式,直到找到足夠記憶體為止,那麼我們的operator new函式就可以設計為:
void *operator new(std::size_t size) throw(std::bad_alloc) { if ( size==0 ) { size = 1; } while (true) { 呼叫malloc等記憶體分配函式來嘗試分配size大小的記憶體; if ( 分配成功 ) return 指向分配得來的記憶體指標; new_handler globalHandler = set_new_handler(0); set_new_handle(globalHandler); if(globalHandler) (*globalHandler)(); else throw std::bad_alloc(); } } |
轉自:http://blog.sina.com.cn/s/blog_9f1c09310101953s.html
使用new分配記憶體失敗時往往會使用asert()終止程式,但是這隻能在除錯模式下abert函式才能有效,在生產模式下,abert只是一個void指令,所以連程式都跳不出來。
而當new操作失敗時,一個好的程式不能簡單的終止程式就行了,而是要嘗試去釋放記憶體 如何能在new操作失敗,在丟擲異常之前先把相應的處理做了呢?這就要用到new_handler了,它是在丟擲exception呼叫的。為了指定這個所謂的“記憶體不足處理函式,new_handler”,client必須呼叫set_new_handler,這是標頭檔案提供的函式,用法: typedef void (*new_handler)() new_handler set_new_handler(new_handler p) throw(); 簡單的程式測試: #include "stdafx.h" #include #include using namespace std; void NoMoreMemory() { cout<<"Unable to statisty request for memory"<<endl; abort(); } int _tmain(int argc, _TCHAR* argv[]) { set_new_handler(NoMoreMemory); int *p=new int[536870911]; return 0; } 注:當operator new 無法滿足記憶體需求時,它會不只一次地呼叫new_handler函式(如果new_handler沒有退出程式的話);它會不斷地呼叫,直到找到足夠的記憶體為止。因此一個良好設計的new_handler函式必須完成下列事情之一: 1)讓更多的記憶體可用。這或許能夠讓operator new 的下一次記憶體配置行為成功。實現此策略的方法之一就是在程式起始時配置一大塊記憶體,然後在new_handler第一次被呼叫時釋放之。如此的釋放動作常常伴隨著某種警告資訊,告訴使用者目前的記憶體已經處於低水位,再來的記憶體需求可能失敗,除非有更多的記憶體回覆自由身。 2)安裝一個不同的new_handler,如果目前的new_handler無法讓更多的記憶體可用,或許它知道另一個new_handler手上我有比較多的資源。果真如此,目前的new_handler就可以安裝另外一個new_handler以取代自己(只要再呼叫一次set_new_handler即可)。當operator new下次呼叫new_handler函式時,它會呼叫最新安裝的那個。 3)卸除掉這個new_handler,也就是說,將null指標傳給set_new_handler,一旦沒有安裝任何new_handler,operator new就會在記憶體配置失敗時丟擲一個型別為std::bad_alloc的exception。 4)丟擲一個exception,型別為std::bad_alloc(或者派生型別)。這樣的exception不會被operator new捕獲,所以它們就會傳動到最初踢出記憶體需求的那個點上 5)不返回,直接呼叫abort或exit https://blog.csdn.net/gudongxian/article/details/48196499