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

劍指 Offer 面試題 2:C++ 單例模式五種實現與最佳實踐

     編輯:關於C++

單例模式看似簡單,實則坑多:線程安全、效能、銷毀時機、異常安全……本文從《劍指 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。


解法二:雙重檢查鎖 Double-Checked Locking (C++11)

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() { /* ... */ }
};

優點:線程安全、減少鎖開銷。
缺點:仍需手動銷毀;高併發下鎖競爭成瓶頸。


解法三:const static 實例(利用靜態初始化線程安全)

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 前單線程完成,天然線程安全且無鎖開銷。
缺點:同樣需手動管理生命週期。


解法四:Meyers Singleton(函數局部靜態變數,C++11 保證線程安全)

class Singleton {
    Singleton() {}
public:
    static Singleton* getInstance() {
        static Singleton instance; // C++11 magic statics
        return &instance;
    }
};

優點:最簡潔、線程安全、自動銷毀(程式結束時)。
致命缺點:無法手動控制銷毀時機,若單例持有檔案鎖、DB 連線等需顯式釋放的資源,會導致資源洩漏或邏輯錯誤。


解法五:最終方案 —— 內嵌 GC 類自動銷毀(推薦生產使用)

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;

原理解析

  1. m_pInstance 為靜態指標,動態分配於堆上。
  2. gc 為類內靜態對象,存放於靜態存儲區,程式結束時 C++ 執行期保證調用其解構函數。
  3. ~GC() 中安全 delete 單例,釋放所有資源(DB、檔案、鎖等)。
  4. 此技巧即 RAII (Resource Acquisition Is Initialization) 的經典應用。

多線程驗證測試

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 類✓高✓(可選)✓通用生產環境首選

掌握單例模式的演進脈絡,不僅能應付面試,更能在實際架構中做出正確權衡。

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