核心技巧:將解構函式宣告為 private,提供 public 的銷毀介面。
class HeapOnly {
public:
void Destroy() const { delete this; }
private:
~HeapOnly() {} // 私有解構函式,外部無法直接 delete,亦無法在棧上創建(離開作用域需自動呼叫解構函式)
};
HeapOnly* ptr = new HeapOnly;
ptr->Destroy(); // 唯一合法銷毀方式
嘗試在棧上創建 HeapOnly obj; 會導致編譯錯誤(無法呼叫私有解構函式)。繼承與組合情況類似,不再贅述。
考慮表達式:
new HeapOnly(* new HeapOnly);
直覺順序:1. 外層 operator new → 2. 外層建構函式 → 3. 內層 operator new → 4. 內層建構函式。
但標準不保證此順序,VC6 實際執行順序為:1. 外層 operator new → 2. 內層 operator new → 3. 外層建構函式 → 4. 內層建構函式。
class HeapTracker {
private:
void* operator new[](size_t size);
typedef const void* RawAddress;
void operator delete[](void* ptr);
public:
void operator delete(void* ptr) {
printf("delete\n");
::operator delete(ptr);
m_address.erase(remove(m_address.begin(), m_address.end(), ptr), m_address.end());
}
void* operator new(size_t size) {
printf("new\n");
void* ptr = ::operator new(size);
m_address.push_back(ptr);
return ptr;
}
HeapTracker() { printf("Constructor!\n"); }
HeapTracker(const HeapTracker&) { printf("Copy Constructor!\n"); }
virtual void Destroy() const { delete this; }
virtual ~HeapTracker() = 0; // 純虛解構函式,強制派生類實作
bool isOnHeap() const {
const void* rawAddress = (const void*)(this);
return find(m_address.begin(), m_address.end(), rawAddress) != m_address.end();
}
private:
static deque m_address;
};
HeapTracker::~HeapTracker() {}
deque HeapTracker::m_address;
class DerivedHeapTracker : public HeapTracker {};
限制:dynamic_cast<const void*>(this) 在 VC6 多重/虛擬繼承下會拋異常,故此實作僅支援單一繼承。
核心技巧:將 operator new/delete 及陣列版本宣告為 private。
class StackOnly {
private:
void* operator new(size_t size);
void* operator new[](size_t size);
void operator delete(void* ptr);
void operator delete[](void* ptr);
public:
StackOnly() { printf("Constructor!\n"); }
StackOnly(const StackOnly&) { printf("Copy Constructor!\n"); }
~StackOnly() {}
};
嘗試 new StackOnly 將導致編譯錯誤(無法存取私有 operator new),只能在棧上創建 StackOnly obj;。簡單有效,無副作用。
Destroy() 呼叫 delete this。operator new/delete(含陣列版)。new/delete 記錄地址,isOnHeap() 查找。注意繼承模式限制與評估順序不確定性。這些技巧展示了 C++ 對物件生命週期與儲存位置的精細控制能力,善用之可避免資源管理錯誤,但需充分理解背後機制與編譯器差異。