单例模式:C++实现与多线程安全
1. 核心定义与作用精准版定义单例模式是一种创建型设计模式确保一个类有且仅有一个实例并向整个系统提供唯一的全局访问点。核心作用控制实例数量严格保证类在程序生命周期内只有一个对象全局访问无需传递对象在代码任意位置获取同一个实例解决实际问题避免多实例竞争资源日志文件、数据库连接、配置文件、打印机减少频繁创建 / 销毁对象的性能开销统一数据状态保证全局数据一致性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;优点天然线程安全由静态初始化保证无锁竞争访问速度快实现简单无坑缺点程序启动就初始化即使不用也占内存大对象会延长启动时间无法做到延迟加载适用场景实例小、一定会使用、追求高性能二、懒汉式延迟初始化核心第一次使用时才创建实例以时间换空间① 基础版线程不安全cppstatic Singleton* getInstance() { if (instance nullptr) { // 多线程下会多次进入 instance new Singleton(); } return instance; }问题高并发下会创建多个实例破坏单例。3. 多线程安全与优化面试核心① 加锁版线程安全但效率低cppstatic Singleton* getInstance() { std::lock_guardstd::mutex lock(mtx); // 每次都加锁 if (instance nullptr) { instance new Singleton(); } return instance; }缺点99% 的情况实例已存在仍要加锁并发性能极差。② 双重检查锁 DCLDouble-Checked Locking面试必考为什么要两次判断第一次判断避免每次都加锁第二次判断防止多线程同时通过第一次判断cppstatic Singleton* getInstance() { if (instance nullptr) { // 第一次检查无锁 std::lock_guardstd::mutex lock(mtx); if (instance nullptr) { // 第二次检查有锁 instance new Singleton(); } } return instance; }致命问题指令重排new分为三步分配内存调用构造函数指针赋值给 instance编译器可能优化为1 → 3 → 2导致其他线程拿到未构造完成的半成品对象程序崩溃。C11 解决方案使用std::atomic 内存屏障禁止重排。③ Meyers 单例C 最优写法强烈推荐C11 特性静态局部变量初始化是线程安全的cppclass 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创建对象。② 必须禁用拷贝构造 赋值运算符cppSingleton(const Singleton) delete; Singleton operator(const Singleton) delete;防止通过拷贝产生新实例。③ 析构函数一般私有化单例由类管理生命周期外部不能 delete。④ 实例类型选择饿汉静态对象栈 / 全局区懒汉静态局部对象推荐不推荐裸指针内存泄漏风险5. 单例生命周期与内存管理静态实例程序结束时自动释放无需手动管理指针实例需要手动写destroy()释放否则内存泄漏依赖顺序多个单例互相依赖时可能出现析构顺序问题最佳方案使用 Meyers 单例完全交给系统管理6. 单例模式优缺点总结优点严格控制唯一实例全局访问方便避免重复创建销毁开销避免资源竞争冲突缺点扩展性差很难改成多例隐藏依赖关系多线程下实现复杂基础版不安全不利于单元测试难以 mock