為什么要重載new/new[] ,delete/delete[]
這是因為指針,確實指針是一件讓人喜歡的東西,用起來如此讓人喜歡,讓人順手。然而小程序我們完全可以避免內存泄露問題,大程序就不那么容易了,然而我們有一種特別好的方法可以跟蹤我們new,和delete動作,找到未被釋放的內存。這是重載new,和delete。
基本原理
內存泄漏就是new出來的內存沒有通過delete合理的釋放掉。new和delete這兩個函數就是關鍵點??梢灾剌dnew和delete,每次new
中開辟一塊內存就用鏈表把這個內存的信息保存下來,每次用delete刪除一塊內存就從鏈表中刪除這塊內存的記錄。
重載的new/new[],delete/delete[]操作符必須是類的靜態成員函數或者全局函數,函數原型如下
void* operator new(size_t size) throw(std::bad_alloc);
// 這里的 size 為分配的內存的總大小
void* operator new[](size_t size) throw(std::bad_alloc);
void operator delete(void* p) throw();
void operator delete[](void* p) throw();
void operator delete(void* p, size_t size) throw();
// 區別于 new[] 的參數 size,這里的 size 并非釋放的內存的總大小
void operator delete[](void* p, size_t size) throw();
另外,我們可以使用不同的參數來重載以上操作符
//第一個參數仍為 size_t
void* operator new(size_t size,const char* file,int line);
使用示例:
string *str= new(__FILE,__LINE__)string;
有些時候,我們只想為指定的類設置自定義的 operator new 成員函數,而不希望影響到子類的工作?!禘ffective C++ Third Edition》提供了如下的方案:
void* Base::operator new(std::size_t size)throw(std::bad_alloc){
//如果大小不為基類大小
if(size!=sizeof(Base))
????? return ::ooperator new(size);
??? ...//自定義大小為基類大小的分配處理
}
對于 operator new[] 來說,我們很難通過上面的方式檢查到底是父類還是子類調用了操作符。通過 operator new[]操作符的參數,我們無法得知分配的元素的個數,無法得知分配的每個元素的大小。operator new[] 的參數 size_t表明的內存分配的大小可能大于需要分配的元素的內存大小之和,因為動態內存分配可能會分配額外的空間來保存數組元素的個數。
這不是個很簡單的事(詳細參考《Effective C++ Third Edition》 Item 51)。operator new 通常這樣編寫:
void* operator new(std::size_t size) throw(std::bad_alloc)
{
using namespace std;
// size == 0 時 new 也必須返回一個合法的指針
if (size == 0)
size = 1;
while (true) {
嘗試進行內存的分配
if (內存分配成功)
return (成功分配的內存的地址);
// 內存分配失敗時,查找當前的 new-handling function
// 因為沒有直接獲取到 new-handling function 的辦法,因此只能這么做
new_handler globalHandler = set_new_handler(0);
set_new_handler(globalHandler);
// 如果存在 new-handling function 則調用
if (globalHandler) (*globalHandler)();
// 不存在 new-handling function 則拋出異常
else throw std::bad_alloc();
}
}
這一些方面是我們需要注意的:operator new 可以接受 size 為 0 的內存分配且返回一個有效的指針;如果存在
new-handling function 那么在內存分配失敗時會調用它并且再次嘗試內存分配;如果不存在 new-handling
function 失敗時拋出 bad_alloc 異常。
要注意的是,一旦設置了 new-handling function內存分配就會無限循環進行下去,為了避免無限循環的發生,new-handling function必須做以下幾件事中的一件(詳細參考《Effective C++ Third Edition》 Item49):讓有更多內存可用、設置另一個能發揮作用的 new-handler、刪除當前的 new handler、拋出一個異常(bad_alloc或者繼承于 bad_alloc)、直接調用 abort() 或者 exit() 等函數。對于 operator delete 的異常處理就簡單一些,只需要保證能夠安全的 delete 空指針即可