operator new 失敗需循環嘗試、調用 new-handler、最終拋 bad_alloc。operator new 需處理 size == 0(返回合法非空指針)。operator delete 必須安全接受空指針(無操作)。new/delete 需處理繼承導致的 size > sizeof(Base) 情況。void* operator new(std::size_t size) throw(std::bad_alloc) {
if (size == 0) size = 1; // 慣例 2
while (true) {
void* p = malloc(size); // 嘗試分配
if (p) return p; // 成功返回
// 失敗:獲取並調用 new-handler
std::new_handler handler = std::set_new_handler(0);
std::set_new_handler(handler);
if (handler) (*handler)(); // 嘗試釋放內存
else throw std::bad_alloc(); // 無 handler 拋異常
}
}
關鍵點:雙重 set_new_handler 實現「獲取當前 handler」(C++ 無直接獲取 API),多線程環境需加鎖。
用於特定類優化(如固定大小塊分配)。繼承陷阱:子類 new Derived 會調用 Base::operator new,但 size == sizeof(Derived)。
class Base {
public:
static void* operator new(std::size_t size) throw(std::bad_alloc) {
if (size != sizeof(Base)) // 慣例 4:非本類大小轉發全局
return ::operator new(size);
// 專屬分配邏輯...
}
static void operator delete(void* p, std::size_t size) throw() {
if (p == 0) return; // 慣例 3
if (size != sizeof(Base)) {
::operator delete(p); // 轉發全局 delete
return;
}
// 專屬釋放邏輯...
}
};
注意:size == 0 無需檢查,因獨立對象最小 1 字節(空類插入 char),sizeof(Base) > 0 恆成立。
size 可能大於元素總大小(編譯器可能存儲數組大小/對齊填充)。Base* p = new Derived;
delete p; // 若 Base::~Base() 非 virtual,傳入 delete 的 size = sizeof(Base) → 錯誤釋放!
// 若 virtual,size = sizeof(Derived) → 正確
這正是條款 7 強調「基類析構函數必須虛化」的底層原因之一。
| 函數 | 必須處理 | 返回/行為 |
|---|---|---|
global new | size=0, 失敗循環+handler | 非空指針或拋 bad_alloc |
global delete | 空指針 | 無操作返回 |
member new | size != sizeof(Class) 轉發 | 同 global |
member delete | 空指針、size != sizeof(Class) 轉發 | 同 global |