在 C++ 代码中经常会遇到需要在整个程序生命周期内仅实例化一次的对象,例如配置管理器、日志系统或数据库连接池。传统的单例实现方式有几种,但在性能和线程安全之间往往需要做权衡。下面介绍一种利用 C++11 标准特性的懒加载单例实现,既简洁又具有极高的线程安全性。
1. 传统实现的缺点
class Singleton {
private:
static Singleton* instance;
Singleton() {}
public:
static Singleton* getInstance() {
if (!instance) {
instance = new Singleton();
}
return instance;
}
};
Singleton* Singleton::instance = nullptr;
- 线程不安全:在多线程环境下,两线程可能同时进入
if (!instance),导致创建两个实例。 - 手动销毁:需要手动管理
delete instance;,否则会导致内存泄漏。 - 初始化成本:每次调用
getInstance()都要检查指针,虽然开销小,但仍不必要。
2. 利用 std::call_once 的实现
#include <mutex>
class Singleton {
private:
Singleton() {}
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
static Singleton* instance;
static std::once_flag initFlag;
static void initSingleton() {
instance = new Singleton();
}
public:
static Singleton& getInstance() {
std::call_once(initFlag, initSingleton);
return *instance;
}
// 如需在程序结束时销毁,可以使用智能指针或托管对象
};
Singleton* Singleton::instance = nullptr;
std::once_flag Singleton::initFlag;
- 线程安全:
std::call_once保证initSingleton仅被调用一次,无论多少线程并发访问。 - 懒加载:只有在第一次调用
getInstance()时才创建实例。 - 无需手动销毁:可在程序退出时使用
atexit或std::unique_ptr自动释放。
3. 现代 C++11 之友好的实现(局部静态变量)
class Singleton {
private:
Singleton() {}
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
public:
static Singleton& getInstance() {
static Singleton instance; // C++11 保证线程安全初始化
return instance;
}
};
- 更简洁:只需要一个
static局部变量。 - 线程安全:从 C++11 起,局部静态变量的初始化是线程安全的。
- 懒加载:首次访问时才实例化。
- 销毁:程序结束时自动销毁,避免泄漏。
4. 对比与选择
| 方案 | 线程安全 | 懒加载 | 代码量 | 销毁方式 |
|---|---|---|---|---|
| 原始单例 | 否 | 否 | 低 | 手动 |
std::call_once |
是 | 是 | 中 | 可手动或自动 |
| 局部静态变量 | 是 | 是 | 低 | 自动 |
在绝大多数现代 C++ 项目中,局部静态变量实现是最推荐的方式。它既满足了懒加载与线程安全,又保持了代码最小化。若你需要在单例中执行复杂的初始化逻辑或需要手动销毁,std::call_once 提供了更灵活的控制。
5. 小结
- 懒加载:只在需要时才创建实例,节省资源。
- 线程安全:使用 C++11 的
std::call_once或局部静态变量即可实现。 - 简洁易用:局部静态变量实现最简洁,适合大多数场景;
std::call_once适用于需要更细粒度控制的情况。
通过以上两种实现方式,你可以在 C++ 项目中轻松安全地使用单例模式,从而提升代码的可维护性与性能。