%%
# 纲要
> 主干纲要、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