《Effective C++》:條款51:編寫new和delete時需固守常規

來源:互聯網
上載者:User

標籤:new   delete   高效c++   c++   

條款 50已經說明為什麼要寫自己的operator new和operator delete,本條款解釋在編寫時遵循什麼規則。

從operator new開始。operator new必須返回正確的值,記憶體不足時必須調用new-handling函數,要有對付零記憶體需求的準備,避免不慎掩蓋正常形式的new–這比較偏近class介面的要求而非實現要求。正常形式的new描述與條款 52。

operator new如果申請記憶體成功,就返回指向那塊記憶體的指標,失敗則遵循條款 49描述,拋出bad_alloc異常。它實際上不知一次嘗試記憶體配置,在每次失敗後都調用new-handling函數。這裡假設new-handling函數能做某些動作將一些記憶體釋放出來。只有指向new-hangling函數的指標為,才會拋出異常。C++規定,即使客戶要求0 byte,operator new也要返回一個合法指標。下面是個non-member operator new的偽碼(pseudocode):

void* operator new(std::size_t size) throw(std::bad_alloc){    using namespace std;    if(size==0){//處理0-byte申請        size=1;    }    while(true){        嘗試分配size bytes;        if(分配成功)            return 指向分配得來的記憶體;        //分配失敗,找到當前的new-handling函數        new_handler globalHandler=set_new_handler(0);        set_new_handler(globalHandler);        if(globalHandler) (*globalHandler)();        else throw std::bad_alloc();    }}

如果在多線程環境下,還需要某種鎖機制,以便處理new-handling函數背後的global資料結構。

上麵包含一個死迴圈,退出此死迴圈唯一辦法就是記憶體配置成功(假設new-handling不為null),所以new-handling函數做的事是:讓更多記憶體可用、安裝另一個new-handler、卸載new-handler、拋出bad_alloc異常(或其衍生類別),或承認失敗直接return。

上面的operator new成員函數可能會被derived classes繼承。注意分配記憶體大小size,它是函數接收的實參。**條款**50提到,定製記憶體 Clerk往往是為了特定的class對象,以此來最佳化,不是為了該class的derived classes。

class Base{public:    static void* operator new(std::size_t size) throw(std::bad_alloc)    ……};class Derived:public Base{……};//假設為重新定義operator newDerived* p=new Derived;//這裡調用了Base::operator new如果是class專屬的operator new,應該改為這樣:void* Base::operator new(std::size_t size) throw(std::bad_alloc){    if(size!=sizeof(Base))        return ::operator new(size);//使用標準的operator new    ……}

如果你打算控制class專屬版的arrays記憶體配置,那麼需要實現operator new[]。編寫operator new[]時,唯一要做的一件事就是分配一塊未加工的記憶體(raw memory),因為你無法對array之內迄今尚未存在的元素對象做任何事。甚至我們無法知道這個array含有多少個元素對象。可能你不知道每個對象多大,因為base class的operator new[]有可能經由繼承被調用,將記憶體配置給derived class對象的array使用。

所以不能再Base::operator new[]中假設每個元素對象大小是sizeof(Base),這樣就是說你不能假設array元素個數是(bytes申請數/sizeof(Base))。此外,傳遞給operator new[]的size_t參數,其值有可能比將輩填對象的記憶體大一些,因為條款 16提過,動態分配的arrays可能包含額外空間用來存放元素個數。

operator delete情況就簡單很多,但是要記住,C++保證刪除指標永遠安全。下面是non-member operator delete的偽碼(pseudocode):

void operator delete(void* rawMemory) throw(){    if(rawMemory==0) return;    歸還rawMemory所指記憶體;}

這個函數的member版本也很簡單,只需多加一個檢查刪除數量。

void Base::operator delete(void rawMemory, std::size_t size) throw(){    if(rawMemory==0) return;    if(size!=sizeof(Base)){        ::operator delete(rawMemory);        return ;    }    歸還rawMemory所指記憶體;    return ;}

如果即將刪除的對象派生自某個base class而後者沒有virtual解構函式,那麼C++傳給operator delete的size_t數值可能不正確。

總結

  • operator new應該內涵死迴圈,並在其中嘗試分配記憶體,如果它無法滿足記憶體需求,就該調用new-handler。它也應該有能力處理0bytes申請。class專屬版本的還應該處理“比正確大小更大的(錯誤)申請”。
  • operator delete應該在收到指標時不做任何事。class專屬版本則還應該處理“比正確大小更大的(錯誤)申請”。

《Effective C++》:條款51:編寫new和delete時需固守常規

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.