首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C++ >> 關於C++

Effective C++ 條款 49:深入理解 new-handler 行為與類別專屬處理器

     編輯:關於C++

標準 new-handler 機制

當 operator new 無法滿足記憶體請求時,會拋出 std::bad_alloc 異常 (舊編譯器可能回傳 null)。拋出前,會呼叫用戶可設定的 new-handler 函式。透過 <new> 標頭的 std::set_new_handler 設定:

namespace std {
    typedef void (*new_handler)();
    new_handler set_new_handler(new_handler p) throw();
}

範例:設定全域處理器


void outOfMem() {
    std::cerr << "Unable to satisfy request for memory\n";
    std::abort();
}
int main() {
    std::set_new_handler(outOfMem);
    int* pBigDataArray = new int[100000000L];
    ...
}

設計良好的 new-handler 必須做到以下之一

  1. 騰出更多記憶體:例如啟動時預留一大塊,handler 釋放供程式使用。
  2. 安裝另一個 handler:呼叫 set_new_handler 替換自己,下一次嘗試會呼叫新 handler。
  3. 卸載 handler:傳遞 null 給 set_new_handler,後續配置失敗直接拋出異常。
  4. 拋出異常:拋出 bad_alloc 或衍生類別,傳播給呼叫端。
  5. 不返回:呼叫 abort() 或 exit() 終止程式。

operator new 會反覆呼叫 handler 直到配置成功或 handler 不返回/拋出異常。

進階需求:類別專屬 new-handler

C++ 不直接支援 per-class handler,但可自行實作。讓每個類別提供自己的 set_new_handler 與 operator new:


class Widget {
public:
    static std::new_handler set_new_handler(std::new_handler p) throw();
    static void* operator new(std::size_t size) throw(std::bad_alloc);
private:
    static std::new_handler currentHandler;
};

關鍵實作:使用 RAII 物件 NewHandlerHolder 確保全域 handler 在配置前後正確切換與還原:


class NewHandlerHolder {
public:
    explicit NewHandlerHolder(std::new_handler nh) : handler(nh) {}
    ~NewHandlerHolder() { std::set_new_handler(handler); }
private:
    std::new_handler handler;
    // 禁止拷貝
};

void* Widget::operator new(std::size_t size) throw(std::bad_alloc) {
    NewHandlerHolder h(std::set_new_handler(currentHandler)); // 安裝 Widget 專屬 handler
    return ::operator new(size); // 呼叫全域 operator new
} // 解構時自動還原全域 handler

通用化:Mixin-style 基類模板 (CRTP)

為避免重複撰寫,將上述邏輯封裝為模板基類 NewHandlerSupport<T>,利用 Curiously Recurring Template Pattern (CRTP) 使每個衍生類別擁有獨立的 currentHandler:


template<typename T>
class NewHandlerSupport {
public:
    static std::new_handler set_new_handler(std::new_handler p) throw();
    static void* operator new(std::size_t size) throw(std::bad_alloc);
private:
    static std::new_handler currentHandler;
};

// Widget 只需繼承即可獲得功能
class Widget : public NewHandlerSupport<Widget> { ... };

模板參數 T 僅用於區分類別,編譯器會為每個實例化產生獨立的靜態成員。這項技術被戲稱為 "Do It For Me" 模式。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved