当前位置: 首页 > news >正文

单例模式:C++实现与多线程安全

1. 核心定义与作用(精准版)

定义

单例模式是一种创建型设计模式,确保一个类有且仅有一个实例,并向整个系统提供唯一的全局访问点

核心作用

  1. 控制实例数量:严格保证类在程序生命周期内只有一个对象
  2. 全局访问:无需传递对象,在代码任意位置获取同一个实例
  3. 解决实际问题
    • 避免多实例竞争资源(日志文件、数据库连接、配置文件、打印机)
    • 减少频繁创建 / 销毁对象的性能开销
    • 统一数据状态,保证全局数据一致性

2. 实现方式:饿汉式 vs 懒汉式(对比 + 代码)

一、饿汉式(静态初始化)

核心:类加载时就创建实例,以空间换时间

cpp

// 饿汉式单例(线程安全,无需加锁) class Singleton { private: // 1. 构造函数私有化 Singleton() = default; // 2. 禁用拷贝、赋值 Singleton(const Singleton&) = delete; Singleton& operator=(const Singleton&) = delete; // 3. 静态实例(类加载时初始化) static Singleton instance; public: // 4. 全局访问点 static Singleton& getInstance() { return instance; } }; // 类外初始化 Singleton Singleton::instance;

优点

  • 天然线程安全(由静态初始化保证)
  • 无锁竞争,访问速度快
  • 实现简单,无坑

缺点

  • 程序启动就初始化,即使不用也占内存
  • 大对象会延长启动时间
  • 无法做到延迟加载

适用场景:实例小、一定会使用、追求高性能


二、懒汉式(延迟初始化)

核心:第一次使用时才创建实例,以时间换空间

① 基础版(线程不安全)

cpp

static Singleton* getInstance() { if (instance == nullptr) { // 多线程下会多次进入 instance = new Singleton(); } return instance; }

问题:高并发下会创建多个实例,破坏单例。


3. 多线程安全与优化(面试核心)

① 加锁版(线程安全,但效率低)

cpp

static Singleton* getInstance() { std::lock_guard<std::mutex> lock(mtx); // 每次都加锁 if (instance == nullptr) { instance = new Singleton(); } return instance; }

缺点:99% 的情况实例已存在,仍要加锁,并发性能极差


② 双重检查锁 DCL(Double-Checked Locking)

面试必考:为什么要两次判断?

  • 第一次判断:避免每次都加锁
  • 第二次判断:防止多线程同时通过第一次判断

cpp

static Singleton* getInstance() { if (instance == nullptr) { // 第一次检查(无锁) std::lock_guard<std::mutex> lock(mtx); if (instance == nullptr) { // 第二次检查(有锁) instance = new Singleton(); } } return instance; }

致命问题:指令重排new分为三步:

  1. 分配内存
  2. 调用构造函数
  3. 指针赋值给 instance

编译器可能优化为:1 → 3 → 2导致其他线程拿到未构造完成的半成品对象,程序崩溃。

C++11 解决方案使用std::atomic+ 内存屏障禁止重排。


③ Meyers 单例(C++ 最优写法,强烈推荐)

C++11 特性:静态局部变量初始化是线程安全的!

cpp

class Singleton { private: Singleton() = default; Singleton(const Singleton&) = delete; Singleton& operator=(const Singleton&) = delete; public: static Singleton& getInstance() { static Singleton instance; // 线程安全,延迟加载 return instance; } };

优点

  • 延迟加载
  • 天然线程安全
  • 无锁、无指针、无内存泄漏
  • 代码极简、无坑

这是 C++ 工程与面试首选写法!


4. C++ 单例必写规范(面试必问)

① 必须私有化构造函数

防止外部new创建对象。

② 必须禁用拷贝构造 + 赋值运算符

cpp

Singleton(const Singleton&) = delete; Singleton& operator=(const Singleton&) = delete;

防止通过拷贝产生新实例。

③ 析构函数一般私有化

单例由类管理生命周期,外部不能 delete。

④ 实例类型选择

  • 饿汉:静态对象(栈 / 全局区)
  • 懒汉:静态局部对象(推荐)
  • 不推荐裸指针(内存泄漏风险)

5. 单例生命周期与内存管理

  1. 静态实例:程序结束时自动释放,无需手动管理
  2. 指针实例:需要手动写destroy()释放,否则内存泄漏
  3. 依赖顺序:多个单例互相依赖时,可能出现析构顺序问题
  4. 最佳方案:使用 Meyers 单例,完全交给系统管理

6. 单例模式优缺点总结

优点

  • 严格控制唯一实例
  • 全局访问方便
  • 避免重复创建销毁开销
  • 避免资源竞争冲突

缺点

  • 扩展性差(很难改成多例)
  • 隐藏依赖关系
  • 多线程下实现复杂(基础版不安全)
  • 不利于单元测试(难以 mock)
http://www.jsqmd.com/news/782511/

相关文章:

  • 如何用OBS虚拟摄像头打破Windows视频应用的限制
  • 2026最新西安正规二手空调售卖服务商实力排行盘点 - 奔跑123
  • 重构视频播放体验:现代化前端架构的3大突破
  • 2026年封罐机行业指南:真空封罐机、充氮封罐机厂家及半自动封罐机生产厂家推荐 - 栗子测评
  • 太原豆包推广怎么选?本地企业真实获客案例参考 - 奔跑123
  • 大模型不只是会聊天:一文看懂 Harness Engineering
  • 替代RCF陶瓷纤维的生产工厂及行业应用解析 - 品牌排行榜
  • 利用Taotoken实现AI应用对不同模型API的快速切换与降级
  • 增量备份为什么还是这么慢?KingbaseES块级永久增量备份给出答案!
  • 基于agentforge框架构建多智能体系统:从原理到实践
  • AI模型优化与部署实战:PrunaAI一站式平台解析
  • Creating a Property Page for Object Properties-自定义 编辑 - 属性
  • 《解决冲突的关键技巧》
  • 2026年唐山外墙清洗、烟道保洁与商业保洁一站式解决方案深度指南 - 企业名录优选推荐
  • 艾尔登法环帧率解锁终极指南:告别60帧限制的完整解决方案
  • 2026年气凝胶保温涂料优质厂家推荐指南 朗缪环保科技(天津)有限公司优选 气凝胶粉体/气凝胶隔热保温涂料/气凝胶涂料/气凝胶保温涂料/气凝胶 - 奔跑123
  • 为Claude Code配置Taotoken以解决封号与Token不足痛点
  • 终极QQ音乐格式转换指南:qmc-decoder快速解密你的加密音乐文件
  • 拼多多电商数据采集终极指南:5分钟搭建专业级爬虫系统
  • 2026中国DevOps平台选型:合规适配与技术演进的双重挑战
  • 2026年必备:学长亲测降至5%!10款降AI率工具红黑榜,免费论文降AI降低AI率指南 - 降AI实验室
  • 2026年四川环保设备选型指南:认准四川怡源溪,合规高效更省心 - 飞花令2022
  • QtScrcpy跨平台投屏控制:5大实战技巧与模块化设计指南
  • 2026年5月佛山公寓床采购指南:为何广东木偶人家具有限公司成为智慧校园首选 - 2026年企业推荐榜
  • 鸣潮自动化终极指南:告别枯燥重复,解放双手的智能助手
  • Agent Skills:让AI助手真正“有技能“的开源标准,正在悄悄改变整个行业
  • 音视频控制传输协议(AVCTP)
  • 良品被判成不良品?做机器视觉的人都该看懂这篇
  • HID设备描述符与端点配置实战解析
  • 为Claude Code配置Taotoken作为稳定后备API服务