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

Effective C++ Item 49:new-handler 行為詳解與類別專屬錯誤處理

     編輯:關於C++

new-handler 基本機制

當 operator new 分配記憶體失敗時,會拋出 bad_alloc 異常,**在此之前**會反覆呼叫由 std::set_new_handler() 設定的錯誤處理函數(new-handler)。

set_new_handler 介面

namespace std {
    typedef void (*new_handler)();
    new_handler set_new_handler(new_handler p) throw();
}
  • 傳入函數指標,返回舊 handler。
  • throw() 表示不拋出異常。

良好設計的 new-handler 應做以下之一

  1. 釋放記憶體使更多可用。
  2. 安裝新的 handler。
  3. 卸載目前 handler(傳 nullptr)。
  4. 拋出 bad_alloc 或衍生異常。
  5. 不返回:abort() 或 exit()。

類別專屬 new-handler 實作

C++ 無內建型別相關 handler,需自行實作:重載 operator new,暫時替換全域 handler。

Widget 範例

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;
};

std::new_handler Widget::currentHandler = 0;

std::new_handler Widget::set_new_handler(std::new_handler p) throw() {
    std::new_handler old = currentHandler;
    currentHandler = p;
    return old;
}

void* Widget::operator new(std::size_t size) throw(std::bad_alloc) {
    std::new_handler globalHandler = std::set_new_handler(currentHandler);
    void* p = ::operator new(size);  // 呼叫全域 new
    std::set_new_handler(globalHandler); // 恢復
    return p;
}

RAII 確保異常安全

若 ::operator new 拋出異常,需確保全域 handler 被恢復 → 使用 RAII 包裝器。

class NewHandlerHolder {
public:
    explicit NewHandlerHolder(std::new_handler nh) : handler(nh) {}
    ~NewHandlerHolder() { std::set_new_handler(handler); }
private:
    std::new_handler handler;
    // 禁用拷貝
    NewHandlerHolder(const NewHandlerHolder&);
    NewHandlerHolder& operator=(const NewHandlerHolder&);
};

void* Widget::operator new(std::size_t size) throw(std::bad_alloc) {
    NewHandlerHolder h(std::set_new_handler(currentHandler));
    return ::operator new(size);  // 異常時析構函數自動恢復 handler
}

通用模板基類

template
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;
};

template
std::new_handler NewHandlerSupport::currentHandler = 0;

template
std::new_handler NewHandlerSupport::set_new_handler(std::new_handler p) throw() {
    std::new_handler old = currentHandler;
    currentHandler = p;
    return old;
}

template
void* NewHandlerSupport::operator new(std::size_t size) throw(std::bad_alloc) {
    NewHandlerHolder h(std::set_new_handler(currentHandler));
    return ::operator new(size);
}

使用:class Widget : public NewHandlerSupport { ... };

模板參數 T 僅為了讓每個類別擁有獨立的靜態 currentHandler。

nothrow new

Widget* p1 = new Widget;           // 失敗拋 bad_alloc
Widget* p2 = new (std::nothrow) Widget; // 失敗回傳 nullptr
if (!p2) { /* 處理失敗 */ }
  • 僅保證記憶體分配不拋異常,建構函數仍可能拋出。
  • C++98 前 new 失敗回傳 0,現標準預設拋異常。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved