Item 50: Understand when it makes sense to replace new and delete.
我們在Item 49中介紹了如何自定義new
的錯誤處理函數,以及如何為你的類重載operator new
。 現在我們回到更基礎的問題,為什麼我們需要自定義operator new
或operator delete
?
new
得到的內存如果沒有delete
會導致內存洩露,而多次delete
又會引發未定義行為。如果自定義operator new
來保存動態內存的地址列表,在delete
中判斷內存是否完整,便可以識別使用錯誤,避免程序崩潰的同時還可以記錄這些錯誤使用的日志。new
和delete
被設計為通用目的(general purpose)的使用方式,通過提供自定義的new
,我們可以手動維護更適合應用場景的存儲策略。new
之前,你可能需要先自定義一個new
來收集地址分配信息,比如動態內存塊大小是怎樣分布的?分配和回收是先進先出FIFO還是後進先出LIFO?operator new
把新申請的內存全部初始化為0.自定義一個operator new
很容易的,比如實現一個支持越界檢查的new
:
static const int signature = 0xDEADBEEF; // 邊界符
typedef unsigned char Byte;
void* operator new(std::size_t size) throw(std::bad_alloc) {
// 多申請一些內存來存放占位符
size_t realSize = size + 2 * sizeof(int);
// 申請內存
void *pMem = malloc(realSize);
if (!pMem) throw bad_alloc();
// 寫入邊界符
*(reinterpret_cast(static_cast(pMem)+realSize-sizeof(int)))
= *(static_cast(pMem)) = signature;
// 返回真正的內存區域
return static_cast(pMem) + sizeof(int);
}
其實上述代碼是有一些瑕疵的:
operator new
應當不斷地調用new handler,上述代碼中沒有遵循這個慣例;double
的起始地址應當是8的整數倍,int
的起始地址應當是4的整數倍。上述代碼可能會引起運行時硬件錯誤。new
和malloc
都遵循這一點,然而我們返回的地址偏移了一個int
。
到此為止你已經看到了,實現一個operator new
很容易,但實現一個好的operator new
卻很難。其實我們還有別的選擇:比如去讀編譯器文檔、內存管理的商業工具、開源內存管理工具等。