单例模式 (Singleton)
概念
单例模式 确保一个类在整个程序生命周期中只有一个实例,并提供一个全局访问点。
它是一种创建型设计模式,广泛用于需要共享资源的场景。
使用场景
- 配置管理器:程序中需要一个全局的配置对象。
- 日志系统:确保日志记录器只有一个实例。
- 数据库连接池:共享一个数据库连接实例。
实现单例模式的要点
- 私有构造函数:防止外部通过
new
操作符创建实例。 - 静态成员变量:保存类的唯一实例。
- 静态方法:提供全局访问点。
- 线程安全:确保多线程环境下实例只有一个。
示例代码:线程安全的单例模式
#include <iostream>
#include <mutex>
// 单例类定义
class Singleton {
private:
// 静态实例指针,用于保存唯一实例
static Singleton* instance;
// 互斥锁,保证多线程环境下的线程安全
static std::mutex mtx;
// 私有构造函数,禁止外部实例化
Singleton() {
std::cout << "Singleton Created!" << std::endl;
}
public:
// 禁止拷贝构造和赋值操作
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
// 静态方法,用于获取唯一实例
static Singleton* getInstance() {
// 双重检查锁定,减少锁的开销
if (instance == nullptr) {
std::lock_guard<std::mutex> lock(mtx); // 加锁
if (instance == nullptr) {
instance = new Singleton(); // 创建唯一实例
}
}
return instance;
}
// 提供一些功能方法
void doSomething() {
std::cout << "Singleton is working!" << std::endl;
}
// 销毁实例(可选)
static void destroyInstance() {
delete instance;
instance = nullptr;
}
};
// 静态成员变量初始化
Singleton* Singleton::instance = nullptr;
std::mutex Singleton::mtx;
// 主函数:测试单例模式
int main() {
// 获取单例实例
Singleton* s1 = Singleton::getInstance();
Singleton* s2 = Singleton::getInstance();
// 调用单例方法
s1->doSomething();
// 检查两个实例是否相同
std::cout << "Are s1 and s2 the same? " << (s1 == s2 ? "Yes" : "No") << std::endl;
// 销毁单例实例
Singleton::destroyInstance();
return 0;
}
代码详解
1. 私有化构造函数
- 目的:禁止通过
new
操作符创建实例。
Singleton() {
std::cout << "Singleton Created!" << std::endl;
}
2. 静态成员变量
- 保存唯一的单例实例,初始值为
nullptr
。
static Singleton* instance;
3. 互斥锁
- 确保在多线程环境下,多个线程不会同时创建实例。
static std::mutex mtx;
4. 静态方法 getInstance
- 提供唯一实例的全局访问点。
- 双重检查锁定:减少锁的开销,提高性能。
static Singleton* getInstance() {
if (instance == nullptr) {
std::lock_guard<std::mutex> lock(mtx);
if (instance == nullptr) {
instance = new Singleton();
}
}
return instance;
}
5. 销毁实例
- 可选:在程序结束时清理单例实例。
static void destroyInstance() {
delete instance;
instance = nullptr;
}
运行结果
执行代码输出:
Singleton Created!
Singleton is working!
Are s1 and s2 the same? Yes
单例模式的优缺点
优点
- 全局唯一实例:确保实例唯一性。
- 延迟初始化:实例在第一次使用时才会创建。
- 线程安全:双重检查锁定保证线程安全。
缺点
- 难以测试:单例模式引入全局状态,可能影响单元测试。
- 不适合频繁访问的场景:线程安全的实现有一定性能开销。
- 内存泄漏风险:如果程序结束时未释放实例,可能造成内存泄漏。
适合扩展的改进
- 懒汉式与饿汉式:可选择延迟初始化(懒汉式)或直接初始化(饿汉式)。
- 智能指针管理:用
std::unique_ptr
管理单例实例,避免内存泄漏。 - 多线程优化:采用 C++11 的
std::call_once
确保线程安全。