問題: 動態內存申請必定成功嗎?ios
- 常見的動態內存分配代碼
C 代碼:c++
void code() { int* p = (int*)malloc(10 * sizeof(int)); if( p != NULL ) { // ... } free(p); }
C++ 代碼:編程
void code() { int* p = new int[10]; if( p != NULL ) { // ... } delete p; }
必須知道的事實函數
- malloc 函數申請失敗時返回 NULL 值
new 關鍵字申請失敗時(根據編譯器不一樣)spa
- 返回 NULL 值 (古代)
- 拋出 std::bad_alloc 異常 (現代)
問題: new 語句中的異常是怎麼拋出來的呢?指針
new 關鍵字在 C++ 規範中的標準行爲code
在堆空間申請足夠的內存對象
成功:內存
- 在獲取的空間中調用構造函數建立對象
- 返回對象的地址
失敗編譯器
- 拋出 std::bad_alloc 異常
new 關鍵字在 C++ 規範中的標準行爲
new 在分配內存時
- 若是空間不足,會調用全局的 new_handler() 函數
- new_handler() 函數中拋出 std::bad_alloc 異常
能夠自定義 new_handler() 函數
- 處理默認的 new 內存分配失敗的狀況
new_handler() 中,能夠手動作一些內存整理的工做,使得更多的堆空間能夠被使用。
new_handler() 函數的替換
- 自定義一個無返回值無參數的函數
調用 set_new_handler() 設置自定義的函數
- 參數類型爲 void(*)()
- 返回值爲默認的 new_handler() 函數入口地址
- new_handler() 的定義和使用
void my_new_handler() { cout << "No enough memory" << endl; } int main(int argc, char* argv[]) { set_new_handler(my_new_handler); // ... return 0; }
公用實驗代碼:
#include <iostream> #include <new> #include <cstdlib> #include <exception> using namespace std; class Test { private: int m_value; public: Test() { cout << "Test()" << endl; m_value = 0; } ~Test() { cout << "~Test()" << endl; } void* operator new (unsigned int size) { cout << "operator new: " << size << endl; // return malloc(size); return NULL; // 注意這裏! 模擬內存申請失敗 } void operator delete(void* p) { cout << "operator delete: " << p << endl; free(p); } void* operator new[] (unsigned int size) { cout << "operator new[]: " << size << endl; // return malloc(size); return NULL; // 注意這裏! 模擬內存申請失敗 } void operator delete[](void* p) { cout << "operator delete[]: " << p << endl; free(p); } };
實驗 1:不一樣編譯器中 new_handler() 行爲
void my_new_handler() { cout << "void my_new_handler()" << endl; } void ex_func_1() { new_handler func = set_new_handler(my_new_handler); // 注意這裏! try { cout << "func = " << func << endl; if( func ) { func(); } } catch(const bad_alloc&) { cout << "catch(catch bad_alloc&)" << endl; } } int main() { ex_func_1(); return 0; }
輸出:[g++] func = 0 輸出:[vc++2010] func = 00000000 輸出:[bcc] func = 0x00401474 catch(catch bad_alloc&) 結論: 默認狀況下,g++,vc++2010 並無提供全局的 new_handler() 函數; gcc 提供了全局的 new_handler() 函數,並拋出了 bad_alloc 異常。
實驗 2:不一樣編譯器 new 失敗行爲
void ex_func_2() { Test* pt = new Test(); cout << "pt = " << pt << endl; delete pt; pt = new Test[5]; cout << "pt = " << pt << endl; delete[] pt; } int main() { ex_func_2(); return 0; }
輸出:[g++] Test() 段錯誤 輸出:[vc++2010] operator new: 4 pt = 00000000 operator new[]: 24 pt = 00000000 輸出:[bcc] operator new: 4 pt = 00000000 operator new[]: 24 pt = 00000000 分析: g++ 編譯生成的可執行文件運行時發生段錯誤: new 重載函數返回 NULL, 因而就在 0 地址處建立對象,調用構造函數。構造函數中,m_value = 0,致使段錯誤。 vc++2010: 若是 new 的重載返回 NULL,對象未建立,構造函數不會被調用 bcc: 若是 new 的重載返回 NULL,對象未建立,構造函數不會被調用
問題:如何跨編譯器統一 new 的行爲, 提升代碼的移植性呢?
解決方案【動態內存分配失敗時,返回空指針】
全局範圍(不推薦)
- 從新定義 new / delete 的實現,不拋出任何異常
- 自定義 new_handler() 函數,不拋出任何異常
類層次範圍
- 重載 new / delete, 不拋出任何異常
單此動態內存分配
- 使用 nothrow 參數,指明 new 不拋出異常
實驗 1: 類層次範圍
#include <iostream> #include <new> #include <cstdlib> #include <exception> using namespace std; class Test { private: int m_value; public: Test() { cout << "Test()" << endl; m_value = 0; } ~Test() { cout << "~Test()" << endl; } void* operator new (unsigned int size) throw() // 注意這裏! { cout << "operator new: " << size << endl; // return malloc(size); return NULL; // 注意這裏! 模擬內存申請失敗 } void operator delete(void* p) { cout << "operator delete: " << p << endl; free(p); } void* operator new[] (unsigned int size) throw() // 注意這裏! { cout << "operator new[]: " << size << endl; // return malloc(size); return NULL; // 注意這裏! 模擬內存申請失敗 } void operator delete[](void* p) { cout << "operator delete[]: " << p << endl; free(p); } }; void ex_func_2() { Test* pt = new Test(); cout << "pt = " << pt << endl; delete pt; pt = new Test[5]; cout << "pt = " << pt << endl; delete[] pt; } int main() { ex_func_2(); return 0; }
輸出:[g++] operator new: 4 pt = 0 operator new[]: 24 pt = 0 輸出:[vc++2010] operator new: 4 pt = 00000000 operator new[]: 24 pt = 00000000 輸出:[bcc] operator new: 4 pt = 00000000 operator new[]: 24 pt = 00000000
實驗 2:單次動態內存分配範圍
#include <iostream> #include <new> #include <cstdlib> #include <exception> using namespace std; void ex_func_3() { int* p = new(nothrow) int[10]; // 注意這裏! cout << "p = " << p << endl; delete p; } int main() { ex_func_3(); return 0; }
輸出: p = 0x8300008
實驗結論
- 不是全部的編譯器都遵循 C++ 的標準規範
- 編譯器可能重定義 new 的實現,並在實現中拋出 bad_alloc 異常
- 編譯器的默認實現中,可能沒有設置全局的 new_handler() 函數
- 對於移植性要求較高的代碼,須要考慮 new 的具體細節
- 不一樣的編譯器在動態內存分配上的實現細節不一樣
- malloc 函數在內存申請失敗時返回 NULL 值
new 關鍵字在內存申請失敗時
- 可能返回 NULL 值
- 可能拋出 bad_alloc 異常
關於 new 的小知識點補充:
在指定的內存空間上建立對象(須要手動調用析構函數)
#include <iostream> using namespace std; int main() { int bb[2] = {0}; struct ST { int x; int y; }; ST* pt = new(bb) ST(); // 注意這裏! pt->x = 1; pt->y = 2; cout << bb[0] << endl; cout << bb[1] << endl; pt->~ST(); // 手動調用析構函數 return 0; }
輸出: 1 2
以上內容參考狄泰軟件學院系列課程,請你們保護原創!