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.tpcf.cn/diannao/86075.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈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; 灵活的可扩展性 海量消息堆积能力 支持顺序消息 支持多种消息过滤方式 支持事务消息 支持回溯消费 支持延时消…