%% # 纲要 > 主干纲要、Hint/线索/路标 # Q&A #### 已明确 #### 待明确 > 当下仍存有的疑惑 **❓<font color="#c0504d"> 有什么问题?</font>** %% # 单例模式 Singleton Pattern **单例模式** 保证了 "**一个类==只有唯一的实例对象==**",并提供**一个全局访问点**来访问该实例。 要点:**私有化构造器** & **单例对象作为静态变量** & **静态方法 `getInstance()`** - (1)**保证一个类只有一个实例对象,防止多于一个对象被实例化**: - (a)**==私有化==构造函数** - (b)在类中将 "**单例对象**" 声明为 "**==静态变量==**" ——声明为一个 "**==静态成员变量==**" or "**`getInstance()` 方法中的==静态局部变量==**"; - (2)**提供全局访问点** - 提供一个 **==静态方法== `getInstance`**,**返回类中单例对象** > [!NOTE] 单例模式类图 > ![[_attachment/02-开发笔记/16-设计模式/单例模式.assets/IMG-单例模式-F3C6C6E194DA18F1684B2017E5125F0E.png|574]] ### 适用场景 - **资源池管理**:线程池、连接池(数据库连接池)等,确保控制对共享资源的访问; - **日志对象** - 缓存 ### 单例模式的两种实现方式 单例模式有两种实现方式,区别在于 "**实例**" 的创建时机不同: - **饿汉模式**(Eager Initialization Singleton):在**类加载时**就创建了单例实例,其天然**线程安全**。 - **懒汉模式**(Lazy Initialization Singleton):**延迟初始化**,仅在**需要时才创建实例**(首次调用 `getInstance()` 时),需要**额外处理==线程安全==问题**。 > [!example] "懒汉模式" 下,需要加锁("双重检查加锁"),保证线程安全。 > > ![[_attachment/02-开发笔记/16-设计模式/单例模式.assets/IMG-单例模式-B9E451E527020998E1CB39AFEA5B0F8C.png|568]] > > 使用 "双重检测" 的原因:如果只有一次检测,意味着**每次调用 `getInstance` 都需要加锁,会严重影响程序性能**。 > 双重检测下,**只会在 "第一次创建单例" 时加锁**,而后单例存在,不满足条件,因此不会再加锁。 <br><br><br> # 实现示例 ### 饿汉模式 ```cpp #include <iostream> /* 单例模式: 饿汉实现 */ class EagerSingleton { public: // 提供一个静态方法, 作为获取唯一实例的 "全局访问点" static EagerSingleton& getInstance() { return instance; } void doSomething() { std::cout << "EagerSingleton is doing something." << std::endl; } private: // 私有化构造函数 EagerSingleton() { std::cout << "EagerSingleton instance created. " << std::endl; } ~EagerSingleton() = default; // 禁止拷贝构造与赋值 EagerSingleton(const EagerSingleton&) = delete; EagerSingleton& operator=(const EagerSingleton&) = delete; // 声明静态成员变量 static EagerSingleton instance; // 对静态成员变量在类内部的声明仅 "声明其存在", 编译器不会为其分配内存, 因此不会报 "非完整类型" 的错误. // 静态成员变量的"定义"要求在类外部完成, 此时编译器才为其分配内存. }; // 静态成员变量定义 & 初始化 EagerSingleton EagerSingleton::instance; int main() { EagerSingleton& instance1 = EagerSingleton::getInstance(); EagerSingleton& instance2 = EagerSingleton::getInstance(); instance1.doSomething(); instance2.doSomething(); } ``` ### 懒汉模式 ```cpp #include <iostream> /* 单例模式: 懒汉实现 * * 通过 "局部静态变量" (推荐)实现. * C++11起, 保证了对"局部静态变量"的初始化是天然线程安全的. */ class LazySingleton { public: // 提供一个静态方法, 作为获取唯一实例的 "全局访问点" static LazySingleton& getInstance() { // 静态局部变量. static LazySingleton instance; // C++11 保证了静态局部变量初始化的线程安全. return instance; } void doSomething() { std::cout << "Singleton instance is doing something." << std::endl; } // 禁止拷贝构造和赋值 LazySingleton(const LazySingleton&) = delete; LazySingleton(LazySingleton&&) = delete; LazySingleton& operator=(const LazySingleton&) = delete; LazySingleton& operator=(LazySingleton&&) = delete; private: // 私有化构造函数 LazySingleton() { std::cout << "Singleton instance created." << std::endl; } // 私有化析构函数. 故禁止在"栈"上构造(栈上对象在超出作用域时, 编译器会自动析构) ~LazySingleton() = default; }; int main() { LazySingleton& instance1 = LazySingleton::getInstance(); LazySingleton& instance2 = LazySingleton::getInstance(); instance1.doSomething(); instance2.doSomething(); } ``` <br><br><br> # 实现 FAQ ##### ❓C++中实现单例模式时,静态成员函数 `getInstance` 应当返回 "**指向单例对象的指针**" 还是返回 "**对单例对象的引用**"? 即两种函数返回类型的区别:`static SingleTon& getInstance();` 与 `static SingleTon* getInstance();` 。 通常来说,**推荐返回 "引用"**,除非**明确具有以下需求**: 1. **需要显式表示单例对象不存在时**,返回指针则可通过 "**空指针**" 进行标识; - 若单例对象需要根据条件动态创建,则可在条件不满足时,返回空指针,表示不存在; ```cpp Singleton* Singleton::getInstance() { if (!condition) return nullptr; static Singleton instance; return &instance; } ``` 2. 需**要实现多态特性**时,例如单例模式涉及到**基类与派生类**,则返回指针可支持动态类型转换,从而实现多态。 <br><br><br> # Buffer ## 闪念 > sudden idea ## 候选资料 > Read it later # ♾️参考资料 # Footnotes