單例模式看似簡單,實則坑多:線程安全、效能、銷毀時機、異常安全……本文從《劍指 Offer》面試題 2 切入,逐步演進五種 C++ 實現,最後給出生產級「最終方案」。
class Singleton {
static Singleton* m_pInstance;
Singleton() {}
public:
static Singleton* getInstance() { return m_pInstance; }
static void destroyInstance() { delete m_pInstance; m_pInstance = nullptr; }
};
Singleton* Singleton::m_pInstance = new Singleton(); // 靜態初始化,主執行緒單線程完成
缺點:多線程下可能創建多個實例;且需手動呼叫 destroyInstance。
class Singleton {
static std::mutex m_mutex;
static Singleton* m_pInstance;
Singleton() {}
public:
static Singleton* getInstance() {
if (!m_pInstance) {
std::lock_guard<std::mutex> lock(m_mutex);
if (!m_pInstance) m_pInstance = new Singleton(); // 二次檢查
}
return m_pInstance;
}
static void destroyInstance() { /* ... */ }
};
優點:線程安全、減少鎖開銷。
缺點:仍需手動銷毀;高併發下鎖競爭成瓶頸。
class Singleton {
static const Singleton* m_pInstance;
Singleton() {}
public:
static Singleton* getInstance() { return const_cast<Singleton*>(m_pInstance); }
};
const Singleton* Singleton::m_pInstance = new Singleton();
原理:C++ 標準保證靜態初始化在 main 前單線程完成,天然線程安全且無鎖開銷。
缺點:同樣需手動管理生命週期。
class Singleton {
Singleton() {}
public:
static Singleton* getInstance() {
static Singleton instance; // C++11 magic statics
return &instance;
}
};
優點:最簡潔、線程安全、自動銷毀(程式結束時)。
致命缺點:無法手動控制銷毀時機,若單例持有檔案鎖、DB 連線等需顯式釋放的資源,會導致資源洩漏或邏輯錯誤。
class Singleton {
Singleton() {}
static Singleton* m_pInstance;
// 內部 GC 類,程式結束時自動析構
class GC {
public:
~GC() {
if (m_pInstance) {
std::cout << "GC: releasing resources...\n";
delete m_pInstance; m_pInstance = nullptr;
}
}
};
static GC gc; // 靜態成員,程式結束自動調用 ~GC()
public:
static Singleton* getInstance() { return m_pInstance; }
};
Singleton* Singleton::m_pInstance = new Singleton();
Singleton::GC Singleton::gc;
m_pInstance 為靜態指標,動態分配於堆上。gc 為類內靜態對象,存放於靜態存儲區,程式結束時 C++ 執行期保證調用其解構函數。~GC() 中安全 delete 單例,釋放所有資源(DB、檔案、鎖等)。void print_instance() { std::cout << Singleton::getInstance() << '\n'; }
void Test1() { // 10 執行緒並發獲取
std::vector<std::thread> ths;
for (int i=0;i<10;++i) ths.emplace_back(print_instance);
for (auto& t:ths) t.join();
}
int main() { Test1(); /* 輸出 10 次相同地址 */ }

| 方案 | 線程安全 | 效能 | 手動銷毀 | 自動銷毀 | 適用場景 |
|---|---|---|---|---|---|
| 1. 單線程 | ✗ | 最高 | ✓ | ✗ | 單線程嵌入式 |
| 2. DCL+鎖 | ✓ | 中(鎖開銷) | ✓ | ✗ | 舊標準、需手動控制 |
| 3. const static | ✓ | 高(無鎖) | ✓ | ✗ | 高效能、需手動控制 |
| 4. Meyers | ✓ | 高 | ✗ | ✓ | 無外部資源、單純邏輯單例 |
| 5. GC 類 | ✓ | 高 | ✓(可選) | ✓ | 通用生產環境首選 |
掌握單例模式的演進脈絡,不僅能應付面試,更能在實際架構中做出正確權衡。