c++面向對象之operator new學習筆記

實際的代碼中已經不多見到new的身影了,即便是比較老的c++編譯器環境, 也提供了auto_ptr管理內存.
可是c++畢竟貼近底層的幾個例證就是對類的封裝沒有增長什麼額外的負擔,c++對象模型仍然是一一映射到連續的內存地址上去;
對於內存的分配方式, 控制權仍然在程序員手裏. 仍是頗有必要熟悉理解一番的.c++

重載operator new好處

  1. 用來檢測運行上的錯誤:好比比較常見的數組越界的段錯誤, 有時候可能系統不會理解dump掉, 那這個錯誤就隱藏了.利用這一點能夠實現內存泄露.程序員

  2. 提供性能: 這點是最重要的,系統提供的模型內存分配行爲一刀切, 而不一樣的數據對內存分配器的需求都不同,系統提供的不必定最優.編程

  3. 收集使用統計數據.數組

set_new_handler

operator new同樣都是<new>提供, 用於在內存分配失敗bad_alloc後調用執行.
系統提供的原型以下, 默認的全局的返回指針爲nullptr不會幹啥或,若是有須要可重載, 或者放在類的內部.多線程

std::new_handler set_new_handler(new_handler);

通常這個指針std::new_handler不會全局賦值, 若是是這樣無疑會影響其餘模塊, 人家好好的內存分配不了最多來個bad_alloc
全局指針一賦值, 就不知道去哪裏去執行啥東西了, 因此, 自用自歸還就成了基本原則, 基本流程以下:函數

  1. 獲取原始的全局指針new_handler, 存儲起來性能

  2. 設置爲新的new_handler使用學習

  3. 本身用完了(做用域內/class內), 把最開始保存的歸還設置給系統.線程

固然, 這裏還涉及多線程的問題, 和當前討論的不相關, 先不考慮. 這種資源的管理最後歸還, 正好可使用類構造/析構函數來完成. 基本形式以下指針

class NewHandlerHolder {
public:
    //構造函數和析構函數共同自動完成了資源的管理.
    explicit NewHandlerHolder(std::new_handler nh) 
        : handler(nh) {}
    ~NewHandlerHolder() {
        std::set_new_handler(handler);
    }   
private:
    std::new_handler handler;
};
 
class Widget 
{
public:
    void *operator new(size_t size) throw(std::bad_alloc);
 
private:
    static std::new_handler currentHandler;
};
 
std::new_handler Widget::currentHandler = 
    [](){
        cout<<"Widget: Unable to satisfy request for memory\n";
        set_new_handler(nullptr);
    };  
 
void *Widget::operator new(size_t size) throw(std::bad_alloc) { 
    cout<<"begin Widget::operator new\n";
    NewHandlerHolder h(std::set_new_handler(currentHandler));
    return ::operator new(size);
}

重載對象operator new

簡化版只給出了重啓了一個operator new形式,按理說,爲了不行爲的不一致(有的調用全局有的調用字構建)仍是所有重載比較穩妥

class INTType
{     
public: 
    INTType():x(0) {}
    static void *operator new(size_t size) throw(std::bad_alloc);
      
    //...
private:
    int x;
};

重載對象operator new最佳實踐

若是系統中有若干個類都有重載全局operator new的需求, 實際上能夠新抽象成一個類做爲基類, 子類繼承接口便可.
而若是要求不只節省代碼, 還要各個類的實例數據獨佔一份, 能夠把類編程模板類,這樣子類實例化數據都是獨立的.

template<typename T>    // T用於實例化用, 類自己並不使用
class NewHandlerSupport {
public:
    void *operator new(size_t size) throw(std::bad_alloc);
    void *operator new[](size_t size) throw(std::bad_alloc);
 
protected:
    static std::new_handler currentHandler;
};
// 使用類的繼承方式
class WidgetHander : public NewHandlerSupport<WidgetHander>

總結

沒需求不要重載全局的任何東西,影響範圍比較大. 最好放到類中重載
如今對於內存的統計和記錄應該都有比較好和成熟的方案, 另外因爲智能指針的引入對於內存的使用也不多發生內存泄露等問題
剩下對於operator new重載的強烈需求只來自於性能上的提高, 這方便仍是能夠繼續深刻學習一下的boost::Pool

參考: <EC49/50/51/52>

相關文章
相關標籤/搜索