當重載帶額外參數的 operator new(placement new)時,必須提供對應參數完全一致的 operator delete(placement delete),否則構造函數拋異常會導致內存洩漏。
Widget* pw = new Widget; 調用 operator new 分配內存,再調用構造函數。若構造拋異常,運行時自動調用對應的 operator delete 釋放內存。標準 new/delete 簽名固定,編譯器知道調用哪個。但 placement new 簽名任意(如 operator new(size_t, ostream&)),運行時無法推導對應的 delete,若缺少 placement delete,則不釋放內存 → 洩漏。
class Widget {
public:
static void* operator new(size_t size, ostream& log) { /* 記錄日誌並分配 */ }
static void operator delete(void* p, size_t size) { ::operator delete(p); } // 普通 delete
static void operator delete(void* p, ostream& log) { ::operator delete(p); } // 必須有的 placement delete
};
若構造拋異常,operator delete(void*, ostream&) 會被調用。普通 delete pw; 仍調用普通 delete,不影響。
類內聲明任何 operator new 會遮蔽全局/基類的所有 new(包括標準版)。導致客戶無法使用 new Widget。解決:建立基類 StandardNewDeleteForms 定義標準 new/delete,派生類用 using 引入:
class StandardNewDeleteForms {
public:
static void* operator new(size_t sz) { return ::operator new(sz); }
static void operator delete(void* p) { ::operator delete(p); }
static void* operator new(size_t sz, void* p) { return ::operator new(sz, p); }
static void operator delete(void* p, void*) { ::operator delete(p); }
static void* operator new(size_t sz, const nothrow_t&) { return ::operator new(sz, nothrow); }
static void operator delete(void* p, const nothrow_t&) { ::operator delete(p); }
};
class Widget : public StandardNewDeleteForms {
public:
using StandardNewDeleteForms::operator new;
using StandardNewDeleteForms::operator delete;
static void* operator new(size_t, ostream&);
static void operator delete(void*, ostream&);
};