單例模式確保類別僅有一個實例並提供全域存取點。經典實現中,實例以 new 動態分配,但何時釋放?誰負責釋放? 是常被忽視的關鍵問題。本文分析釋放時機難題,提出「垃圾回收器」嵌套類別方案,利用程式結束時靜態成員自動解構特性,實現零干預、異常安全的單例資源回收。
class CSingleton {
public:
static CSingleton* GetInstance() {
if (!m_pInstance) m_pInstance = new CSingleton();
return m_pInstance;
}
private:
CSingleton() {}
static CSingleton* m_pInstance;
};
CSingleton* CSingleton::m_pInstance = nullptr;
m_pInstance 指向的記憶體未釋放 → 記憶體洩漏檢測工具報警main 結束前 delete GetInstance():醜陋、易忘、難保證後續不再存取C++ 保證:程式結束時,所有靜態儲存期物件的解構函數會被自動呼叫 (逆序建構順序)。
class CSingleton {
public:
static CSingleton* GetInstance() {
if (!m_pInstance) m_pInstance = new CSingleton();
return m_pInstance;
}
private:
CSingleton() {}
static CSingleton* m_pInstance;
// 嵌套清理類別 - 唯一職責:解構時刪除單例實例
class CGarbo {
public:
~CGarbo() {
if (CSingleton::m_pInstance) {
delete CSingleton::m_pInstance;
CSingleton::m_pInstance = nullptr;
}
}
};
static CGarbo Garbo; // 靜態成員:程式結束自動解構
};
// 靜態成員定義
CSingleton* CSingleton::m_pInstance = nullptr;
CSingleton::CGarbo CSingleton::Garbo;
| 特性 | 說明 |
|---|---|
| 零使用者干預 | 客戶代碼無需呼叫任何清理函數 |
| 異常安全 | 即使 main 拋出異常,靜態物件解構仍保證執行 |
| 封裝性佳 | CGarbo 為私有嵌套類別,防止外部濫用 |
| 單一定義 | 清理邏輯集中在單例類別內部,符合高內聚 |
| 解構順序 | 依賴靜態物件解構順序 (跨編譯單元順序未定義,但單例內部相對順序固定) |
class Singleton {
public:
static Singleton& GetInstance() {
static Singleton instance; // C++11 保證執行緒安全、延遲初始化
return instance;
}
private:
Singleton() = default;
~Singleton() = default;
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
};
instance 自動管理生命週期,無需手動 new/delete
class Singleton {
public:
static std::shared_ptr GetInstance() {
static std::weak_ptr weak;
if (auto shared = weak.lock()) return shared;
auto created = std::shared_ptr(new Singleton(),
[](Singleton* p) { delete p; }); // 自定義刪除器
weak = created;
return created;
}
};
class Singleton {
public:
static void DestroyInstance() { // 僅在極少數需求下提供
if (m_pInstance) { delete m_pInstance; m_pInstance = nullptr; }
}
};
「垃圾回收器」嵌套類別方案優雅解決了經典單例的釋放難題,展示了 C++ RAII 機制與靜態物件生命週期的深度結合。現代 C++ (C++11+) 推薦使用區域靜態變數 (Meyer's Singleton),語法更簡潔、語意更清晰、執行緒安全有保障。選擇哪種方案,取決於 C++ 標準版本、執行緒需求、跨模組部署等工程約束。