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

C++ 強制或禁止物件在 Heap 上創建:技巧與陷阱全解析

     編輯:關於C++

一、強制物件只能在 Heap 上創建

核心技巧:將解構函式宣告為 private,提供 public 的銷毀介面。

class HeapOnly {
public:
    void Destroy() const { delete this; }
private:
    ~HeapOnly() {}  // 私有解構函式,外部無法直接 delete,亦無法在棧上創建(離開作用域需自動呼叫解構函式)
};

HeapOnly* ptr = new HeapOnly;
ptr->Destroy(); // 唯一合法銷毀方式

嘗試在棧上創建 HeapOnly obj; 會導致編譯錯誤(無法呼叫私有解構函式)。繼承與組合情況類似,不再贅述。

二、判斷物件是否位於 Heap:編譯器不保證的評估順序

考慮表達式:

new HeapOnly(* new HeapOnly);

直覺順序:1. 外層 operator new → 2. 外層建構函式 → 3. 內層 operator new → 4. 內層建構函式。
但標準不保證此順序,VC6 實際執行順序為:1. 外層 operator new → 2. 內層 operator new → 3. 外層建構函式 → 4. 內層建構函式。

追蹤 Heap 分配地址的實作(單一繼承)

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 多重/虛擬繼承下會拋異常,故此實作僅支援單一繼承。

三、禁止物件在 Heap 上創建

核心技巧:將 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;。簡單有效,無副作用。

總結

  • 僅 Heap:私有解構函式 + public Destroy() 呼叫 delete this。
  • 僅 Stack:私有 operator new/delete(含陣列版)。
  • Heap 判斷:重載 new/delete 記錄地址,isOnHeap() 查找。注意繼承模式限制與評估順序不確定性。

這些技巧展示了 C++ 對物件生命週期與儲存位置的精細控制能力,善用之可避免資源管理錯誤,但需充分理解背後機制與編譯器差異。

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