7、爲內存不足的狀況預做準備
1、使用new時內存不足
2、如何處理內存不足
#define NEW(PTR, TYPE) \
try {(PTR) = new TYPE;}\
catch(std::bad_alloc&) {assert(0);}
如上,assert()在cassert頭文件,是一個宏,只有在debug模式下才有用,會檢查接收的算式結果是否爲非0值,如果不是會發出錯誤信息並調用abort。但是,在非debug模式下也有可能發生這種情況,且上述只針對new的一種形式,即new T。typedef void (*new_handler)();
new_handler set_new_handler(new_handler p) throw();
如上,new_handler是一個typedef,是一個函數指針,指向的函數沒有參數沒有返回值。而set_new_handler是一個函數,入參與返回值均爲new_handler,入參指向內存不足時調用的函數,返回值指向之前的new_handler。可以這樣使用set_new_handler():void noMoreMemory()
{
cerr << "Unable to satisfy request for memory\n";
abort();
}
int main()
{
set_new_handler(noMoreMemory);
int *pBigDataArray = new int[100000000];
...
}
當operator new無法配置內存時,他會不停地調用new_handler,直到找到內存。故一個良好的new_handler必須完成以下事情之一:3、class專屬的operator new
class X
{
public:
static new_handler set_new_handler(new_handler p);
static void * operator new(size_t size);
private:
static new_handler currentHandler;
}
爲class X處理內存配置失敗的問題,應將這些設爲類的static成員,初始化在定義之外,如可以在實現文件中。
new_handler X::currentHandler;//初始化爲0, 即NULL
set_new_handler將獲得的new_handler保存下來,返回之前保存的new_handler:
new_handler X::set_new_handler(new_handler p)
{
new_handler oldHandler = currentHandler;
currentHandler = p;
return oldHandler;
}
最後,operator new完成這些事情:
1.調用std::set_new_handler,將class的new_handler設爲新的new_handler,將原來的new_handler保存爲globalHandler。
2.調用::operator new,若內存配置失敗,調用設置好的new_handler,若最終還是沒有配置內存,則拋出std::bad_alloc的異常,被X::operator new捕捉,調用std::set_new_handler,將new_handler恢復爲globalHandler保存的new_handler,再將異常傳播出去並結束函數。
3.若::operator new內存配置成功,同樣調用std::set_new_handler,將new_handler恢復爲globalHandler保存的new_handler。然後傳回一個指針,指向那塊內存。
void * X::operator new(size_t size)
{
new_handler globalHandler = std::set_new_handler(currentHandler);
void *memory;
try
{
memory = ::operator new(size);
}
catch(std::bad_alloc&)
{
std::set_new_handler(globalHandler);
throw;
}
std::set_new_handler(globalHandler);
return memory;
}
以上,事實上,可以添加模板來實現代碼的重用,只需添加template<typename T>,修改類型即可。然後定義的類由此模板類派生即可。這樣的操作方便簡單,但有可能引入多重繼承的一些問題,這裏暫且不提。
因此,使用operator new時務必關注內存配置失敗的問題,處理此問題的方法之一是判斷返回值是否爲0,這對於拋出異常形式的new會測試失敗;而另外方法之一是用set_new_handler,他對於拋出異常形式的new和nothrow形式的new都適用。