標籤:style blog color 代碼 div c++ new size
自己重寫operator new時(條款10解釋了為什麼有時要重寫它),很重要的一點是函數提供的行為要和系統預設的operator new一致。實際做起來也就是:要有正確的傳回值;可用記憶體不夠時要調用出錯處理函數(見條款7);處理好0位元組記憶體請求的情況。此外,還要避免不小心隱藏了標準形式的new,不過這是條款9的話題。
有關傳回值的部分很簡單。如果記憶體配置請求成功,就返回指向記憶體的指標;如果失敗,則遵循條款7的規定拋出一個std::bad_alloc類型的異常。operator new實際上會不只一次地嘗試著去分配記憶體,它要在每次失敗後調用出錯處理函數,還期望出錯處理函數能想辦法釋放別處的記憶體。只有在指向出錯處理函數的指標為空白的情況下,operator new才拋出異常。
c++標準要求,即使在請求分配0位元組記憶體時,operator new也要返回一個合法指標。
非類成員形式的operator new的虛擬碼看起來會象下面這樣:
void * operator new(size_t size) // operator new還可能有其它參數{ if (size == 0) { // 處理0位元組請求時, size = 1; // 把它當作1個位元組請求來處理 } while (1) { 分配size位元組記憶體; if (分配成功) return (指向記憶體的指標); // 分配不成功,找出當前出錯處理函數 new_handler globalhandler = set_new_handler(0); set_new_handler(globalhandler); if (globalhandler) (*globalhandler)(); else throw std::bad_alloc(); }}
處理零位元組請求的技巧在於把它作為請求一個位元組來處理。
operator new經常會被子類繼承,這會導致某些複雜性。大多數針對類所寫的operator new(包括條款10中的那種)都是只為特定的類設計的,不是為所有的類,也不是為它所有的子類設計的。這意味著,對於一個類x的operator new來說,函數內部的行為在涉及到對象的大小時,都是精確的sizeof(x):不會大也不會小。但由於存在繼承,基類中的operator new可能會被調用去為一個子類對象分配記憶體:
class base {public: static void * operator new(size_t size); ...};class derived: public base // derived類沒有聲明operator new{ ... }; // derived *p = new derived; // 調用base::operator new
如果base類的operator new不想費功夫專門去處理這種情況——這種情況出現的可能性不大——那最簡單的辦法是把這個“錯誤”數量的記憶體配置請求轉給標準operator new來處理,象下面這樣:
void * base::operator new(size_t size){ if (size != sizeof(base)) // 如果數量“錯誤”,讓標準operator new return ::operator new(size); // 去處理這個請求 // ... // 否則處理這個請求}
如果想控制基於類的數組的記憶體配置,必須實現operator new的數組形式——operator new[]
重寫operator new(和operator new[])時所有要遵循的常規就這些。對於operator delete(以及它的夥伴operator delete[]),情況更簡單。所要記住的只是,c++保證刪除null 指標永遠是安全的,所以你要充分地應用這一保證。下面是非類成員形式的operator delete的虛擬碼:
void operator delete(void *rawmemory){ if (rawmemory == 0) return; //如果指標為空白,返回 // 釋放rawmemory指向的記憶體; return;}
假設類的operator new將“錯誤”大小的分配請求轉給::operator new,那麼也必須將“錯誤”大小的刪除請求轉給::operator delete:
class base { // 和前面一樣,只是這裡聲明了public: // operator delete static void * operator new(size_t size); static void operator delete(void *rawmemory, size_t size); ...};void base::operator delete(void *rawmemory, size_t size){ if (rawmemory == 0) return; // 檢查null 指標 if (size != sizeof(base)) { // 如果size"錯誤", ::operator delete(rawmemory); // 讓標準operator來處理請求 return; } 釋放指向rawmemory的記憶體; return;}