在 C++ 中,单例模式(Singleton Pattern)是一种设计模式,用于确保一个类只有一个实例,并提供一个全局访问点。以下是几种实现单例模式的常见方法:
1. 懒汉式(Lazy Initialization)
懒汉式单例模式在第一次调用时创建实例。
优点:
- 延迟初始化:只有在需要时才创建实例,节省资源。
缺点:
- 线程不安全:多线程环境下可能会创建多个实例。
示例:
class Singleton {
public:
static Singleton& getInstance() {
static Singleton instance; // 局部静态变量,线程安全(C++11 及以上)
return instance;
}
void doSomething() {
std::cout << "Doing something..." << std::endl;
}
private:
Singleton() {} // 私有构造函数
Singleton(const Singleton&) = delete; // 删除拷贝构造函数
Singleton& operator=(const Singleton&) = delete; // 删除赋值运算符
};
2. 饿汉式(Eager Initialization)
饿汉式单例模式在程序启动时创建实例。
优点:
- 线程安全:实例在程序启动时创建,避免了多线程问题。
缺点:
- 资源浪费:即使不需要实例,也会在程序启动时创建。
示例:
class Singleton {
public:
static Singleton& getInstance() {
return instance;
}
void doSomething() {
std::cout << "Doing something..." << std::endl;
}
private:
Singleton() {} // 私有构造函数
Singleton(const Singleton&) = delete; // 删除拷贝构造函数
Singleton& operator=(const Singleton&) = delete; // 删除赋值运算符
static Singleton instance; // 静态成员变量
};
Singleton Singleton::instance; // 在程序启动时初始化
3. 双重检查锁定(Double-Checked Locking)
双重检查锁定是一种线程安全的懒汉式单例模式。
优点:
- 线程安全:通过双重检查锁定机制,确保多线程环境下只创建一个实例。
- 延迟初始化:只有在需要时才创建实例。
缺点:
- 实现复杂:需要手动管理锁和内存屏障。
示例:
#include <mutex>
class Singleton {
public:
static Singleton& getInstance() {
if (instance == nullptr) { // 第一次检查
std::lock_guard<std::mutex> lock(mutex); // 加锁
if (instance == nullptr) { // 第二次检查
instance = new Singleton();
}
}
return *instance;
}
void doSomething() {
std::cout << "Doing something..." << std::endl;
}
private:
Singleton() {} // 私有构造函数
Singleton(const Singleton&) = delete; // 删除拷贝构造函数
Singleton& operator=(const Singleton&) = delete; // 删除赋值运算符
static Singleton* instance; // 静态成员变量
static std::mutex mutex; // 互斥锁
};
Singleton* Singleton::instance = nullptr; // 初始化为空
std::mutex Singleton::mutex; // 初始化互斥锁
4. Meyers’ Singleton(局部静态变量)
Meyers’ Singleton 是一种线程安全的懒汉式单例模式,利用局部静态变量的特性。
优点:
- 线程安全:C++11 及以上标准保证局部静态变量的初始化是线程安全的。
- 延迟初始化:只有在需要时才创建实例。
- 实现简单:代码简洁,易于理解。
缺点:
- 依赖 C++11 及以上标准:需要编译器支持 C++11 或更高版本。
示例:
class Singleton {
public:
static Singleton& getInstance() {
static Singleton instance; // 局部静态变量,线程安全(C++11 及以上)
return instance;
}
void doSomething() {
std::cout << "Doing something..." << std::endl;
}
private:
Singleton() {} // 私有构造函数
Singleton(const Singleton&) = delete; // 删除拷贝构造函数
Singleton& operator=(const Singleton&) = delete; // 删除赋值运算符
};
5. 使用 std::call_once
std::call_once
是 C++11 提供的一种线程安全的初始化机制。
优点:
- 线程安全:确保多线程环境下只初始化一次。
- 延迟初始化:只有在需要时才创建实例。
缺点:
- 依赖 C++11 及以上标准:需要编译器支持 C++11 或更高版本。
示例:
#include <mutex>
class Singleton {
public:
static Singleton& getInstance() {
std::call_once(initFlag, []() {
instance = new Singleton();
});
return *instance;
}
void doSomething() {
std::cout << "Doing something..." << std::endl;
}
private:
Singleton() {} // 私有构造函数
Singleton(const Singleton&) = delete; // 删除拷贝构造函数
Singleton& operator=(const Singleton&) = delete; // 删除赋值运算符
static Singleton* instance; // 静态成员变量
static std::once_flag initFlag; // 初始化标志
};
Singleton* Singleton::instance = nullptr; // 初始化为空
std::once_flag Singleton::initFlag; // 初始化标志
6. 使用智能指针
使用 std::unique_ptr
或 std::shared_ptr
管理单例实例,避免手动管理内存。
优点:
- 自动管理内存:避免内存泄漏。
- 线程安全:结合
std::call_once
或局部静态变量实现线程安全。
示例:
#include <memory>
#include <mutex>
class Singleton {
public:
static std::shared_ptr<Singleton> getInstance() {
std::call_once(initFlag, []() {
instance = std::shared_ptr<Singleton>(new Singleton());
});
return instance;
}
void doSomething() {
std::cout << "Doing something..." << std::endl;
}
private:
Singleton() {} // 私有构造函数
Singleton(const Singleton&) = delete; // 删除拷贝构造函数
Singleton& operator=(const Singleton&) = delete; // 删除赋值运算符
static std::shared_ptr<Singleton> instance; // 静态成员变量
static std::once_flag initFlag; // 初始化标志
};
std::shared_ptr<Singleton> Singleton::instance = nullptr; // 初始化为空
std::once_flag Singleton::initFlag; // 初始化标志
总结
方法 | 线程安全 | 延迟初始化 | 实现复杂度 | 依赖标准 |
---|---|---|---|---|
懒汉式 | 不安全 | 是 | 简单 | 无 |
饿汉式 | 安全 | 否 | 简单 | 无 |
双重检查锁定 | 安全 | 是 | 复杂 | 无 |
Meyers’ Singleton | 安全 | 是 | 简单 | C++11 及以上 |
std::call_once | 安全 | 是 | 中等 | C++11 及以上 |
智能指针 | 安全 | 是 | 中等 | C++11 及以上 |
选择建议
- 简单场景:推荐使用 Meyers’ Singleton(局部静态变量)。
- 需要手动管理内存:推荐使用 双重检查锁定 或
std::call_once
。 - 需要自动管理内存:推荐使用 智能指针。
THE END
暂无评论内容