【C++设计模式】第一篇:单例模式(Singleton)​

devtools/2025/3/6 17:36:09/

注意:复现代码时,确保 VS2022 使用 C++17/20 标准以支持现代特性。

确保全局唯一实例的线程安全实现


1. 模式定义与用途​

核心目标:保证一个类仅有一个实例,并提供全局访问点。
常见场景:

  • 日志系统(避免多个日志实例竞争文件资源)
  • 配置管理(统一读取和修改全局配置)
  • 硬件接口访问(如打印机设备控制)

2. 线程安全的现代 C++ 实现​

2.1 方法一:局部静态变量(Meyers’ Singleton,C++11起线程安全)

#include <iostream>class Singleton {
public:// 删除拷贝构造函数和赋值操作符Singleton(const Singleton&) = delete;Singleton& operator=(const Singleton&) = delete;// 获取唯一实例static Singleton& getInstance() {static Singleton instance; // 线程安全(C++11起)return instance;}void logMessage(const std::string& message) {std::cout << "Log: " << message << std::endl;}private:// 私有构造函数,禁止外部创建实例Singleton() = default;
};//测试代码
int main() {Singleton::getInstance().logMessage("System started.");Singleton::getInstance().logMessage("User logged in.");// 验证实例地址唯一Singleton& s1 = Singleton::getInstance();Singleton& s2 = Singleton::getInstance();std::cout << "Addresses equal? " << (&s1 == &s2 ? "Yes" : "No") << std::endl; // 输出:Yesreturn 0;
}

代码解析:

  • 使用局部静态变量 static Singleton instance,C++11 标准保证其初始化线程安全。
  • 删除拷贝构造函数和赋值操作符,防止意外复制实例。
  • 私有构造函数确保外部无法直接创建对象。

2.2 方法二:std::call_once(适用于需要动态初始化的场景)

#include <iostream>
#include <mutex>
#include <memory>class Singleton {
public:Singleton(const Singleton&) = delete;Singleton& operator=(const Singleton&) = delete;static Singleton& getInstance() {std::call_once(initFlag, []() {instance = std::unique_ptr<Singleton>(new Singleton());});return *instance;}void logMessage(const std::string& message) {std::cout << "Log: " << message << std::endl;}private:Singleton() = default;static std::unique_ptr<Singleton> instance;static std::once_flag initFlag;
};// 静态成员初始化
std::unique_ptr<Singleton> Singleton::instance = nullptr;
std::once_flag Singleton::initFlag;// 测试代码(同上)

代码解析:

  • std::call_once 保证初始化代码仅执行一次,即使多线程环境下也安全。
  • 使用 std::unique_ptr 管理实例,避免内存泄漏。

3. 传统双检锁(Double-Checked Locking)的问题与改进​

旧版 C++(非线程安全)​:

// 警告:C++11 前的实现可能存在线程安全问题!
Singleton* Singleton::getInstance() {if (instance == nullptr) {               // 第一次检查std::lock_guard<std::mutex> lock(mutex);if (instance == nullptr) {           // 第二次检查instance = new Singleton();}}return instance;
}

问题:

  • 内存读写顺序问题(指令重排可能导致未初始化完成的对象被访问)。

改进(C++11 起使用原子变量)​:

#include <atomic>class Singleton {static std::atomic<Singleton*> instance;static std::mutex mutex;public:static Singleton* getInstance() {Singleton* tmp = instance.load(std::memory_order_acquire);if (tmp == nullptr) {std::lock_guard<std::mutex> lock(mutex);tmp = instance.load(std::memory_order_relaxed);if (tmp == nullptr) {tmp = new Singleton();instance.store(tmp, std::memory_order_release);}}return tmp;}
};

4. 应用场景示例:日志系统

// 在 Singleton 类中添加日志文件操作
#include <fstream>class Singleton {// ...(同上)
private:std::ofstream logFile;Singleton() {logFile.open("app.log", std::ios::app);}public:void logMessage(const std::string& message) {if (logFile.is_open()) {logFile << message << std::endl;}}~Singleton() {logFile.close();}
};

5. 单例模式的优缺点

优点​​缺点
全局唯一实例,避免资源冲突隐藏依赖关系,降低代码可测试性
延迟初始化(节省内存)长期持有资源可能影响程序退出行为

6. 总结与调试技巧

  • 验证线程安全:在 VS2022 中使用多线程调试,观察实例地址是否唯一。
  • 查看静态变量生命周期:通过断点检查局部静态变量的初始化时机。
  • 禁用拷贝操作:务必删除拷贝构造函数和赋值操作符以防止意外复制

http://www.ppmy.cn/devtools/165044.html

相关文章

Ubuntu20.04 在离线机器上安装 NVIDIA Container Toolkit

步骤 1.下载4个安装包 Index of /nvidia-docker/libnvidia-container/stable/ nvidia-container-toolkit-base_1.13.5-1_amd64.deb libnvidia-container1_1.13.5-1_amd64.deb libnvidia-container-tools_1.13.5-1_amd64.deb nvidia-container-toolkit_1.13.5-1_amd64.deb 步…

spring-ioc-bean

本文重点在于充分应用 Spring 提供的 IoC 特性&#xff0c;介绍如何创建一个好用的 Bean。基础篇不涉及后置处理器、 BeanDefinition 以及 Spring 加载原理相关的知识。 引入 ioc 的起源 ** 接口与实现类的需求变更 ** &#xff1a;最初的静态工厂模式。** 反射机制 ** &…

Unix Domain Socket和eventfd

在Linux开发中&#xff0c;Unix Domain Socket和eventfd是两种不同的通信机制&#xff0c;它们的设计目标和适用场景有显著差异。以下分点解释并配合示例说明&#xff1a; 一、Unix Domain Socket&#xff08;UDS&#xff09; 1. 是什么&#xff1f; 一种**本地进程间通信&am…

【数据库】关系代数

关系代数 一、关系代数的概念二、关系代数的运算2.1 并、差、交2.2 投影、选择2.3 笛卡尔积2.4 连接2.5 重命名2.6 优先级 一、关系代数的概念 关系代数是一种抽象的数据查询语言用对关系的运算来表达查询 运算对象&#xff1a;关系运算符&#xff1a;4类运算结果&#xff1a;…

大白话html第五章HTML5 新增表单元素和属性

大白话html第五章HTML5 新增表单元素和属性 HTML5 给表单带来了很多新的小伙伴&#xff0c;让我们收集用户信息变得更方便、更智能。 新增表单元素 <input type"date">&#xff1a;这个就像一个自带日历的小框框&#xff0c;用户可以直接在里面选择日期&…

11 Oracle Golden Gate 高可用解决方案:Golden Gate 助力企业保障业务连续性

文章目录 Oracle Golden Gate 高可用解决方案&#xff1a;Golden Gate 助力企业保障业务连续性一、Oracle Golden Gate基本概念二、设计异地灾备策略2.1 需求分析2.2 网络规划2.3 部署架构 三、实施异地灾备策略3.1 环境准备3.2 配置Golden Gate3.3 验证与测试 四、数据保护策略…

Mac mini M4安装nvm 和node

先要安装Homebrew&#xff08;如果尚未安装&#xff09;。在终端中输入以下命令&#xff1a; /bin/zsh -c "$(curl -fsSL https://gitee.com/cunkai/HomebrewCN/raw/master/Homebrew.sh)" 根据提示操作完成Homebrew的安装。 安装nvm。在终端中输入以下命令&#xf…

Docker 部署开源项目HivisionIDPhotos详细教程

本章教程主要介绍如何在Docker上部署开源项目HivisionIDPhotos。 一、HivisionIDPhotos简介 HivisionIDPhoto 旨在开发一种实用、系统性的证件照智能制作算法。 它利用一套完善的AI模型工作流程,实现对多种用户拍照场景的识别、抠图与证件照生成。 HivisionIDPhoto 可以做到:…