轉自: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指令,所以連程序都跳不出來。