C++ 单例模式:饿汉与懒汉模式

发布时间:2026/5/21 7:37:13

C++ 单例模式:饿汉与懒汉模式 设计模式是软件工程中沉淀的 “代码兵法”而单例模式是最常用的创建型设计模式之一。它的核心目标是保证一个类在整个程序生命周期中只有一个实例并提供全局访问点适用于配置管理、资源池、日志管理等需要全局唯一实例的场景。本文将详细讲解单例模式的两种核心实现饿汉模式、懒汉模式包括设计思路、代码实现、优缺点及线程安全优化。一、单例模式的核心价值在复杂系统中单例模式能解决以下关键问题资源统一管理如服务器配置文件读取单例对象统一加载配置避免多实例重复读取 / 修改导致的数据不一致性能优化避免频繁创建 / 销毁重量级对象如数据库连接、网络套接字降低系统开销全局访问提供统一的实例访问入口简化模块间的交互。单例模式的实现需满足三个核心约束构造函数私有化禁止外部直接创建对象禁用拷贝构造和赋值运算符防止通过拷贝生成多实例提供静态成员函数作为获取唯一实例的全局入口。二、饿汉模式程序启动即初始化1. 核心思路“饿汉” 顾名思义 ——程序启动时main 函数执行前就创建唯一实例无论后续是否使用。利用全局静态变量的初始化特性天然保证实例唯一性。2. 完整代码实现#include iostream #include string using namespace std; // 单例模式 - 饿汉模式 class Singleton { public: // 全局访问点返回唯一实例的指针/引用 static Singleton* GetInstance() { return m_instance; } // 示例测试成员函数 void PrintInfo() { cout Singleton Instance Address: this endl; } private: // 1. 私有化构造函数禁止外部创建对象 Singleton() { cout 饿汉模式Singleton 构造函数调用 endl; } // 2. 禁用拷贝构造和赋值运算符C98/C11 两种写法 // C98私有 只声明不定义 // Singleton(const Singleton); // Singleton operator(const Singleton); // C11显式删除推荐 Singleton(const Singleton) delete; Singleton operator(const Singleton) delete; // 3. 静态成员变量程序启动时初始化保证唯一 static Singleton m_instance; }; // 全局静态成员初始化main函数前执行 Singleton Singleton::m_instance; // 测试代码 int main() { // 获取两个实例指针验证地址一致 Singleton* ptr1 Singleton::GetInstance(); Singleton* ptr2 Singleton::GetInstance(); ptr1-PrintInfo(); // 输出Singleton Instance Address: 0xXXXXXXX ptr2-PrintInfo(); // 输出与ptr1完全相同 // 验证拷贝/赋值被禁用编译报错 // Singleton obj *ptr1; // 拷贝构造被删除 // *ptr2 *ptr1; // 赋值运算符被删除 return 0; }3. 优缺点分析优点缺点实现简单无线程安全问题静态变量初始化由编译器保证原子性程序启动时即初始化若实例未被使用则浪费内存多线程高并发场景下响应快无需加锁多个单例类的初始化顺序无法控制无内存泄漏风险静态变量由系统自动销毁若实例构造耗时如加载大文件会导致程序启动慢4. 适用场景单例对象构造耗时短、占用资源少程序运行期间必然会使用该实例多线程高并发场景避免懒汉模式的加锁开销。三、懒汉模式延迟加载按需初始化1. 核心思路“懒汉” 即 “懒加载”——第一次调用 GetInstance 时才创建实例程序启动时不初始化避免资源浪费。核心挑战是解决多线程下的线程安全问题。2. 线程安全的完整实现Double-Check 优化#include iostream #include string #include mutex #include thread using namespace std; // 单例模式 - 懒汉模式线程安全版 class Singleton { public: // 全局访问点Double-Check 加锁保证效率线程安全 static Singleton* GetInstance() { // 第一次检查避免每次调用都加锁提升效率 if (nullptr m_pInstance) { m_mtx.lock(); // 加锁保证多线程下只有一个线程创建实例 // 第二次检查防止多个线程等待锁后重复创建 if (nullptr m_pInstance) { m_pInstance new Singleton(); cout 懒汉模式Singleton 构造函数调用 endl; } m_mtx.unlock(); } return m_pInstance; } // 内嵌垃圾回收类解决单例对象的内存泄漏问题 class CGarbo { public: ~CGarbo() { // 程序结束时自动调用析构释放单例对象 if (Singleton::m_pInstance) { delete Singleton::m_pInstance; Singleton::m_pInstance nullptr; cout 懒汉模式Singleton 析构函数调用 endl; } } }; // 静态垃圾回收对象程序结束时系统自动销毁 static CGarbo Garbo; // 示例测试成员函数 void PrintInfo() { cout Singleton Instance Address: this endl; } private: // 1. 私有化构造函数 Singleton() {} // 2. 禁用拷贝构造和赋值运算符 Singleton(const Singleton) delete; Singleton operator(const Singleton) delete; // 3. 静态成员变量 static Singleton* m_pInstance; // 单例对象指针初始化为nullptr static mutex m_mtx; // 互斥锁保证线程安全 }; // 静态成员初始化 Singleton* Singleton::m_pInstance nullptr; Singleton::CGarbo Singleton::Garbo; mutex Singleton::m_mtx; // 多线程测试函数 void ThreadFunc() { Singleton* ptr Singleton::GetInstance(); ptr-PrintInfo(); } // 测试代码 int main() { // 创建多个线程验证实例唯一性 thread t1(ThreadFunc); thread t2(ThreadFunc); thread t3(ThreadFunc); t1.join(); t2.join(); t3.join(); return 0; }3. 核心设计解析1Double-Check 加锁机制第一次检查无锁若实例已创建直接返回避免每次调用都加锁提升高并发场景下的效率加锁保证多线程下只有一个线程进入实例创建逻辑第二次检查加锁后防止多个线程等待锁后重复创建实例比如线程 A 创建实例前线程 B 已等待锁A 创建完成后 B 若不检查会再次创建。2内嵌垃圾回收类CGarbo懒汉模式使用new创建堆对象若手动调用delete易遗漏导致内存泄漏。内嵌垃圾回收类的核心逻辑CGarbo是静态成员对象程序结束时系统会自动调用其析构函数析构函数中删除单例对象保证资源正常释放。4. 优缺点分析优点缺点延迟加载仅在第一次使用时初始化节省内存实现复杂需处理线程安全和内存泄漏问题程序启动快无初始化负载Double-Check 加锁存在微小的性能开销远低于每次加锁多个单例类的初始化顺序可自由控制C11 前可能存在指令重排导致的线程安全隐患需 volatile 优化5. 适用场景单例对象构造耗时久、占用资源多如加载插件、初始化网络连接程序运行期间可能不使用该实例对程序启动速度要求高。四、饿汉 vs 懒汉核心对比特性饿汉模式懒汉模式初始化时机程序启动时main 前第一次调用 GetInstance 时线程安全天然安全编译器保证需加锁Double-Check内存占用启动即占用可能浪费按需占用更节省程序启动速度可能变慢构造耗时快无初始化负载实现复杂度简单几行代码复杂加锁 垃圾回收初始化顺序无法控制可自由控制五、进阶优化C11 简化版懒汉模式C11 规定局部静态变量的初始化是线程安全的可利用这一特性实现极简的懒汉模式class Singleton { public: static Singleton GetInstance() { // 局部静态变量第一次调用时初始化线程安全 static Singleton instance; return instance; } // 禁用拷贝构造和赋值运算符 Singleton(const Singleton) delete; Singleton operator(const Singleton) delete; private: Singleton() {} // 私有化构造函数 }; // 测试调用 Singleton instance Singleton::GetInstance();该方案无需加锁、无需垃圾回收类兼顾简洁性和线程安全是 C11 及以上版本的首选懒汉模式实现。六、注意事项禁用拷贝 / 赋值无论哪种模式必须禁用拷贝构造和赋值运算符否则可能通过Singleton obj *GetInstance();生成多实例线程安全懒汉模式务必处理线程安全问题避免多线程下创建多个实例内存泄漏懒汉模式需保证堆对象正常释放推荐内嵌垃圾回收类或 C11 局部静态变量单例滥用单例模式会增加代码耦合性非必要场景如可通过参数传递的对象避免使用。总结单例模式的核心是构造函数私有化 禁用拷贝 静态全局访问点保证实例唯一性饿汉模式简单、线程安全但可能浪费资源适用于轻量级实例懒汉模式延迟加载、节省资源需处理线程安全和内存泄漏C11 可通过局部静态变量简化实现选择哪种模式取决于实例的构造开销、使用频率及程序启动性能要求。

相关新闻