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

C++ 保護型解構函數:禁止棧上建構、強制堆分配與工廠模式的經典實現

     編輯:關於C語言

問題背景:為什麼要限制物件建構位置?

C++ 物件可建於棧(自動生命週期)或堆(手動 new/delete)。某些場景必須強制堆分配:

  • 物件由智能指標/工廠統一管理
  • 物件需透過 shared_from_this 獲取自身共享指標
  • 異步回調中物件生命週期超出當前作用域
  • 插件架構、跨 DLL 介面傳遞

三種嘗試與失敗分析

經典實現:工廠模式 + 保護型解構函數


class HeapOnly {
protected:
    HeapOnly() = default;           // 保護構造:僅衍生類/友元可建構
    ~HeapOnly() = default;          // 保護解構:關鍵點!

public:
    // 靜態工廠方法:唯一合法建構入口
    static HeapOnly* create() {
        return new HeapOnly();      // 類內部可訪問 protected 構造
    }

    // 成員銷毀方法:唯一合法銷毀入口
    void destroy() {
        delete this;                // 類內部可訪問 protected 解構
    }

    // 禁用拷貝/移動(可選,視需求)
    HeapOnly(const HeapOnly&) = delete;
    HeapOnly& operator=(const HeapOnly&) = delete;
};

// 使用方式
int main() {
    // HeapOnly obj;           // 編譯錯誤:棧上建構需呼叫 public 解構函數
    // HeapOnly* p = new HeapOnly(); // 編譯錯誤:外部無法訪問 protected 構造

    HeapOnly* p = HeapOnly::create(); // 正確:工廠建構
    p->destroy();                      // 正確:成員銷毀
    return 0;
}

核心機制深度解析

為什麼棧上建構會失敗?

棧上物件離開作用域時,編譯器自動插入解構函數呼叫。此呼叫發生在使用者代碼(非類成員/友元),無法存取 protected 解構函數 → 編譯錯誤。

為什麼堆上 delete 能成功?

delete p 分兩步:1. 呼叫解構函數 2. 釋放記憶體。若 p 型別為 HeapOnly*,解構函數呼叫發生在類成員函數(如 destroy())或友元函數中,合法存取 protected 解構函數。

繼承支援

衍生類解構函數需呼叫基類解構函數。protected 允許衍生類存取,完美支援繼承體系。若為 private,衍生類無法呼叫 → 編譯錯誤。

現代 C++ 改良:搭配智能指標與自定義刪除器


class HeapOnly {
protected:
    ~HeapOnly() = default;
public:
    static std::unique_ptr create() {
        return {new HeapOnly(), [](HeapOnly* p){ p->destroy(); }};
    }
    void destroy() { delete this; }
};

// 使用:自動 RAII 管理
auto ptr = HeapOnly::create(); // 智能指標離開作用域自動呼叫 destroy()

應用場景與變體

  • 單例模式:構造函數 private,靜態 getInstance() 返回靜態區物件(非堆)
  • Reference Counting 基類:解構函數 protected,release() 內 delete this
  • COM 介面:AddRef/Release,Release 歸零時 delete this
  • 異步任務物件:任務佇列持有智能指標,完成後自動銷毀

總結

protected 解構函數是「強制堆分配、禁止棧建構、保留繼承能力」的精準槓桿。配合工廠方法與 destroy()(或智能指標自定義刪除器),構成 C++ 資源管理的經典模式。理解其編譯期存取檢查機制,是掌握 C++ 物件生命週期控制的關鍵一課。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
嘗試結果原因
構造函數 private失敗堆上 new 也無法呼叫私有構造函數
解構函數 private失敗繼承類無法呼叫基類私有解構函數 → 無法繼承
解構函數 protected成功棧上建構需編譯器生成隱式解構呼叫 → 存取失敗;堆上 delete 由類成員/友元呼叫 → 合法;繼承類可呼叫 → 支援繼承