C++單例模式教學指南

C++單例模式完整教學指南

📚 目錄

  1. [單例模式基礎概念]
  2. [經典單例實現及問題]
  3. [現代C++推薦實現]
  4. [高級話題:雙重檢查鎖]
  5. [實戰應用與最佳實踐]
  6. [總結與選擇指南]

1. 單例模式基礎概念

1.1 什么是單例模式?

單例模式(Singleton Pattern)是一種創建型設計模式,確保一個類只有一個實例,并提供全局訪問點。

1.2 應用場景

  • 日志系統:全局統一的日志記錄器
  • 配置管理:程序配置信息的統一管理
  • 數據庫連接池:管理數據庫連接資源
  • 線程池:管理線程資源
  • 緩存系統:全局數據緩存

1.3 單例模式的核心要求

  • ? 只能有一個實例
  • ? 提供全局訪問點
  • ? 禁止拷貝構造
  • ? 禁止賦值操作
  • ? 線程安全(多線程環境)

2. 經典單例實現及問題

2.1 樸素實現(? 有問題)

class Singleton {
private:static Singleton* instance;Singleton() = default;public:static Singleton* getInstance() {if (instance == nullptr) {instance = new Singleton();  // 線程不安全!}return instance;}
};Singleton* Singleton::instance = nullptr;

問題分析:

  • ? 線程不安全:多線程可能創建多個實例
  • ? 內存泄漏:new出來的對象永遠不會被delete
  • ? 沒有禁止拷貝和賦值

2.2 加鎖版本(? 安全但性能差)

class Singleton {
private:static Singleton* instance;static std::mutex mtx;Singleton() = default;public:Singleton(const Singleton&) = delete;Singleton& operator=(const Singleton&) = delete;static Singleton* getInstance() {std::lock_guard<std::mutex> lock(mtx);  // 每次都加鎖,性能差if (instance == nullptr) {instance = new Singleton();}return instance;}
};Singleton* Singleton::instance = nullptr;
std::mutex Singleton::mtx;

改進點:

  • ? 線程安全
  • ? 禁止拷貝和賦值
  • ? 性能問題:每次調用都要加鎖
  • ? 仍有內存泄漏

3. 現代C++推薦實現

3.1 Meyers單例(? 最推薦)

class Singleton {
private:Singleton() = default;~Singleton() = default;public:// 禁止拷貝和賦值Singleton(const Singleton&) = delete;Singleton& operator=(const Singleton&) = delete;static Singleton& getInstance() {static Singleton instance;  // C++11起線程安全return instance;}// 示例方法void doSomething() {std::cout << "Singleton working..." << std::endl;}
};

優勢分析:

  • ? 線程安全:C++11保證局部靜態變量初始化的線程安全性
  • ? 性能優秀:初始化后調用無需加鎖
  • ? 自動析構:程序結束時自動清理
  • ? 代碼簡潔:無需手動管理內存和鎖

3.2 通用單例模板(? 可復用)

template <typename T>
class Singleton {
public:// 禁止拷貝和賦值Singleton(const Singleton&) = delete;Singleton& operator=(const Singleton&) = delete;static T& getInstance() {static T instance;return instance;}protected:Singleton() = default;~Singleton() = default;
};// 使用示例
class Logger : public Singleton<Logger> {friend class Singleton<Logger>;  // 允許Singleton訪問私有構造函數private:Logger() { std::cout << "Logger initialized" << std::endl; }public:void log(const std::string& message) {std::cout << "[LOG] " << message << std::endl;}
};// 使用方法
int main() {Logger::getInstance().log("Hello Singleton!");return 0;
}

4. 高級話題:雙重檢查鎖

4.1 什么是雙重檢查鎖(DCLP)?

雙重檢查鎖定(Double-Checked Locking Pattern)是一種優化技術,減少鎖的使用頻率:

if (!instance) {           // 第一次檢查(無鎖)std::lock_guard<std::mutex> lock(mtx);if (!instance) {       // 第二次檢查(加鎖后)instance = new Singleton();}
}

4.2 傳統DCLP的問題

class UnsafeSingleton {
private:static std::shared_ptr<UnsafeSingleton> instance;static std::mutex mtx;public:static std::shared_ptr<UnsafeSingleton> getInstance() {if (!instance) {  // 問題:可能讀到"半成品"對象std::lock_guard<std::mutex> lock(mtx);if (!instance) {instance = std::make_shared<UnsafeSingleton>();  // 非原子操作}}return instance;}
};

問題根源: std::make_shared的執行過程不是原子的:

  1. 分配內存
  2. 調用構造函數
  3. 設置指針值

其他線程可能在步驟2和3之間讀到未完全構造的對象!

4.3 安全的DCLP實現

template <typename T>
class SafeDCLPSingleton {
private:static std::atomic<std::shared_ptr<T>> instance;static std::mutex mtx;protected:SafeDCLPSingleton() = default;~SafeDCLPSingleton() = default;public:SafeDCLPSingleton(const SafeDCLPSingleton&) = delete;SafeDCLPSingleton& operator=(const SafeDCLPSingleton&) = delete;static std::shared_ptr<T> getInstance() {// 原子讀取auto temp = instance.load(std::memory_order_acquire);if (!temp) {std::lock_guard<std::mutex> lock(mtx);temp = instance.load(std::memory_order_relaxed);if (!temp) {temp = std::make_shared<T>();// 原子寫入instance.store(temp, std::memory_order_release);}}return temp;}
};// 靜態成員定義
template <typename T>
std::atomic<std::shared_ptr<T>> SafeDCLPSingleton<T>::instance{nullptr};template <typename T>
std::mutex SafeDCLPSingleton<T>::mtx;

5. 實戰應用與最佳實踐

5.1 日志系統實現

class Logger : public Singleton<Logger> {friend class Singleton<Logger>;private:std::mutex log_mtx;std::ofstream log_file;Logger() {log_file.open("application.log", std::ios::app);}~Logger() {if (log_file.is_open()) {log_file.close();}}public:enum LogLevel { INFO, WARNING, ERROR };void log(LogLevel level, const std::string& message) {std::lock_guard<std::mutex> lock(log_mtx);auto now = std::chrono::system_clock::now();auto time_t = std::chrono::system_clock::to_time_t(now);log_file << "[" << std::put_time(std::localtime(&time_t), "%Y-%m-%d %H:%M:%S") << "] ";switch (level) {case INFO: log_file << "[INFO] "; break;case WARNING: log_file << "[WARN] "; break;case ERROR: log_file << "[ERROR] "; break;}log_file << message << std::endl;log_file.flush();}
};// 使用示例
int main() {Logger::getInstance().log(Logger::INFO, "Application started");Logger::getInstance().log(Logger::ERROR, "Something went wrong");return 0;
}

5.2 配置管理器

class ConfigManager : public Singleton<ConfigManager> {friend class Singleton<ConfigManager>;private:std::unordered_map<std::string, std::string> config_data;mutable std::shared_mutex config_mtx;ConfigManager() {loadFromFile("config.ini");}void loadFromFile(const std::string& filename) {// 簡化的配置文件加載邏輯config_data["database_url"] = "localhost:3306";config_data["max_connections"] = "100";config_data["debug_mode"] = "true";}public:std::string getValue(const std::string& key, const std::string& default_value = "") const {std::shared_lock<std::shared_mutex> lock(config_mtx);auto it = config_data.find(key);return (it != config_data.end()) ? it->second : default_value;}void setValue(const std::string& key, const std::string& value) {std::unique_lock<std::shared_mutex> lock(config_mtx);config_data[key] = value;}int getIntValue(const std::string& key, int default_value = 0) const {std::string str_value = getValue(key);return str_value.empty() ? default_value : std::stoi(str_value);}bool getBoolValue(const std::string& key, bool default_value = false) const {std::string str_value = getValue(key);return str_value == "true" || str_value == "1";}
};

5.3 永不銷毀的單例(特殊場景)

template <typename T>
class NeverDestroySingleton {
public:NeverDestroySingleton(const NeverDestroySingleton&) = delete;NeverDestroySingleton& operator=(const NeverDestroySingleton&) = delete;static T& getInstance() {static T* instance = new T();  // 永遠不會被析構return *instance;}protected:NeverDestroySingleton() = default;~NeverDestroySingleton() = default;
};

使用場景:

  • 防止靜態析構順序問題
  • 程序退出時必須保持可用的組件(如日志系統)

注意: 這種方式會導致內存"泄漏",但在某些場景下是可接受的。


6. 總結與選擇指南

6.1 各種實現方式對比

實現方式線程安全性能內存管理復雜度推薦指數
樸素實現?????????
加鎖版本???????
Meyers單例?????????????
單例模板??????????????
安全DCLP?????????????

6.2 選擇建議

🎯 通用場景(90%的情況)

推薦:Meyers單例或單例模板

class MyClass : public Singleton<MyClass> {friend class Singleton<MyClass>;// 實現...
};
🎯 高并發系統

推薦:安全DCLP + atomic

  • 網絡服務器
  • 游戲引擎
  • 實時系統
🎯 特殊需求
  • 需要延遲銷毀:NeverDestroy單例
  • 需要Mock測試:依賴注入替代單例
  • 跨DLL使用:特殊處理或避免使用

6.3 使用注意事項

? 最佳實踐
  1. 優先使用Meyers單例(局部靜態變量)
  2. 總是禁止拷貝構造和賦值操作
  3. 考慮使用模板提高代碼復用性
  4. 在構造函數中完成所有初始化工作
  5. 注意異常安全性
? 常見誤區
  1. 不要手動管理單例的生命周期
  2. 不要在單例的析構函數中訪問其他單例
  3. 避免在單例中使用其他單例(循環依賴)
  4. 不要將單例用作全局變量的替代品

6.4 代碼模板(直接使用)

// 文件:singleton.h
#pragma once
#include <mutex>
#include <memory>// 通用單例模板
template <typename T>
class Singleton {
public:Singleton(const Singleton&) = delete;Singleton& operator=(const Singleton&) = delete;Singleton(Singleton&&) = delete;Singleton& operator=(Singleton&&) = delete;static T& getInstance() {static T instance;return instance;}protected:Singleton() = default;virtual ~Singleton() = default;
};// 使用宏簡化定義(可選)
#define SINGLETON_CLASS(ClassName) \friend class Singleton<ClassName>; \private: \ClassName(); \~ClassName() = default;

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/86075.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/86075.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/86075.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

使用xdocreport導出word

之前java總用freemaker進行導出&#xff0c;但是改xml實在是太繁瑣了&#xff0c;這次找了另一個工具進行體驗. 一、簡單導出 pom引入 <dependency><groupId>fr.opensagres.xdocreport</groupId><artifactId>fr.opensagres.xdocreport.core</arti…

vscode里如何用git

打開vs終端執行如下&#xff1a; 1 初始化 Git 倉庫&#xff08;如果尚未初始化&#xff09; git init 2 添加文件到 Git 倉庫 git add . 3 使用 git commit 命令來提交你的更改。確保在提交時加上一個有用的消息。 git commit -m "備注信息" 4 …

C++.OpenGL (2/64)你好,三角形(Hello Triangle)

你好,三角形(Hello Triangle) 繪制流程概覽 #mermaid-svg-MvIGIovxiuKVfzy8 {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-MvIGIovxiuKVfzy8 .error-icon{fill:#552222;}#mermaid-svg-MvIGIovxiuKVfzy8 .error…

汽車安全體系:FuSa、SOTIF、Cybersecurity 從理論到實戰

汽車安全&#xff1a;功能安全&#xff08;FuSa&#xff09;、預期功能安全&#xff08;SOTIF&#xff09;與網絡安全(Cybersecurity) 從理論到實戰的安全體系 引言&#xff1a;自動駕駛浪潮下的安全挑戰 隨著自動駕駛技術從L2向L4快速演進&#xff0c;汽車安全正從“機械可靠…

N2語法 列挙、話題提出

1&#xff0c;&#xff5e;やら&#xff5e;やら  接続&#xff1a;名詞、辭書形  意味&#xff1a;…啦…啦&#xff08;列舉代表性的事物&#xff09;  例文&#xff1a;     家に帰って料理やら洗濯やら何もしなければならない。     帰國前、買い物やら荷造りや…

深入理解React Hooks的原理與實踐

深入理解React Hooks的原理與實踐 引言 React Hooks 自 2018 年 React 16.8 發布以來&#xff0c;徹底改變了前端開發者的編碼方式。它通過函數式組件提供了狀態管理和生命周期等功能&#xff0c;取代了傳統的類組件&#xff0c;使得代碼更加簡潔、復用性更強。然而&#xff…

RockyLinux9.6搭建k8s集群

博主介紹&#xff1a;?全網粉絲5W&#xff0c;全棧開發工程師&#xff0c;從事多年軟件開發&#xff0c;在大廠呆過。持有軟件中級、六級等證書。可提供微服務項目搭建與畢業項目實戰&#xff0c;博主也曾寫過優秀論文&#xff0c;查重率極低&#xff0c;在這方面有豐富的經驗…

鏈游技術破壁:NFT資產確權與Play-to-Earn經濟模型實戰

鏈游技術破壁&#xff1a;NFT資產確權與Play-to-Earn經濟模型實戰 ——從「投機泡沫」到「可持續生態」的技術重構 一、NFT確權技術革新&#xff1a;從鏈上存證到動態賦權 跨鏈確權架構 全鏈互操作協議&#xff1a;采用LayerZero協議實現以太坊裝備與Solana土地的跨鏈組合&…

Java下載文件(特殊字符編碼處理)

當你在這個問題上花費了數小時而解決不了&#xff0c;你才會知道這篇文章對你的幫助 import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.io.Resource; import org.springframework.http.HttpEntity; import org.springframewo…

TDengine 高級功能——讀緩存

簡介 在物聯網&#xff08;IoT&#xff09;和工業互聯網&#xff08;IIoT&#xff09;大數據應用場景中&#xff0c;實時數據的價值往往遠超歷史數據。企業不僅需要數據處理系統具備高效的實時寫入能力&#xff0c;更需要能快速獲取設備的最新狀態&#xff0c;或者對最新數據進…

YOLO在C#中的完整訓練、驗證與部署方案

YOLO在C#中的完整訓練、驗證與部署方案 C# 在 YOLO 部署上優勢明顯&#xff08;高性能、易集成&#xff09;&#xff0c;但訓練能力較弱&#xff0c;通常需結合 Python 實現。若項目對開發效率要求高且不依賴 C# 生態&#xff0c;建議全程使用 Python&#xff1b;若需深度集成…

pikachu靶場通關筆記17 CSRF關卡03-CSRF(Token)

目錄 一、CSRF原理 二、CSRF Token 三、源碼分析 四、CSRF Token tracker插件 1、插件簡介 2、插件安裝 五、滲透實戰 1、用戶登錄 2、修改個人信息 3、bp攔截報文 4、bp改報文探測 5、配置CSRF-Token-Tracer 6、bp改包成功 7、查看CSRF Token Tracker配置 本系…

C#面試問題81-100

85. What are anonymous types? 匿名類型是在需要的地方直接定義的類型&#xff0c;甚至都 不給它命名。它非常適合我們這種用例——類型小且臨時&#xff0c;而且我們無意在其 他地方使用它 匿名類型是直接從 System.Object 派生的類對象。它們不能轉換為任何 其他類型。●…

【Ragflow】25.Ragflow-plus開發日志:excel文件解析新思路/公式解析適配

引言 RagflowPlus v0.3.0 版本中&#xff0c;增加了對excel文件的解析支持&#xff0c;但收到反饋&#xff0c;說效果并不佳。 以下測試文件內容來自群友反饋提供&#xff0c;數據已脫敏處理。 經系統解析后&#xff0c;分塊效果如下&#xff1a; 可以看到&#xff0c;由于該…

VS2022下C++ Boost庫安裝與使用使用

一.Boost概述 1.簡介 Boost 是一個廣泛使用的 C 庫集合&#xff0c;提供了許多高質量、可移植、高效的工具和組件&#xff0c;被視為 C 標準庫的延伸。自 1998 年成立以來&#xff0c;Boost 已成為 C 社區的核心資源&#xff0c;許多 Boost 庫通過實踐驗證后被納入 C 標準&am…

內嵌式mqtt server

添加moquette依賴 <dependency><groupId>io.moquette</groupId><artifactId>moquette-broker</artifactId><version>0.17</version><exclusions><exclusion><groupId>org.slf4j</groupId><artifactId>…

php執行后報502,無錯誤提示的排查和解決

文章目錄 一、闡述問題二、開始排查1.執行代碼展示2.PHP層面排查問題3.系統層面排查問題1. 分析系統日志2. core dump 分析2.1 core dump 是什么2.2 core dump 配置 并 生成 core 文件2.3 gdb 解析 core 文件 4. 問題解決 三、贈送內容四、總結 一、闡述問題 這個問題花了我起…

MySQL 核心知識點解析

最近正在復習Java八股&#xff0c;所以會將一些熱門的八股問題&#xff0c;結合ai與自身理解寫成博客便于記憶 InnoDB 和 MyISAM 的區別 特性InnoDBMyISAM事務支持支持ACID事務不支持事務鎖機制行級鎖表級鎖外鍵支持支持不支持崩潰恢復有crash-safe能力無存儲結構聚簇索引非…

CppCon 2015 學習:Comparison is not simple, but it can be simpler.

What is comparison? 這段文字是從計算機科學、編譯器設計或系統優化的角度來定義和評價“比較&#xff08;comparison&#xff09;”這個操作&#xff1a; 1. Pervasive&#xff08;無處不在&#xff09; 比較操作在編程中極為常見&#xff0c;存在于&#xff1a; 分支語句&…

RocketMQ入門5.3.2版本(基于java、SpringBoot操作)

一、RocketMQ概述 RocketMQ是一款由阿里巴巴于2012年開源的分布式消息中間件&#xff0c;旨在提供高吞吐量、高可靠性的消息傳遞服務。主要特點有&#xff1a; 靈活的可擴展性 海量消息堆積能力 支持順序消息 支持多種消息過濾方式 支持事務消息 支持回溯消費 支持延時消…