单例模式(Singleton)是指在整个程序运行期间,只允许存在一个实例,并且可以被全局访问。实现线程安全的单例模式在多线程环境下尤为重要,下面介绍几种常见的实现方式。
1. 局部静态变量(C++11 及以上)
class Singleton {
public:
static Singleton& getInstance() {
static Singleton instance; // C++11 起保证线程安全
return instance;
}
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
private:
Singleton() = default;
~Singleton() = default;
};
- 优点:代码简洁,使用 C++11 的线程安全初始化特性,零成本。
- 缺点:如果需要延迟销毁(如程序结束前不销毁),则无法控制。
2. 双重检查锁(Meyers 单例改进版)
class Singleton {
public:
static Singleton* getInstance() {
if (!instance) {
std::lock_guard<std::mutex> lock(mutex);
if (!instance) {
instance = new Singleton();
}
}
return instance;
}
// 其它成员...
private:
Singleton() = default;
~Singleton() = default;
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
static Singleton* instance;
static std::mutex mutex;
};
Singleton* Singleton::instance = nullptr;
std::mutex Singleton::mutex;
- 优点:在高并发读场景下性能较好。
- 缺点:实现复杂,容易出错;C++11 的
static初始化已足够安全。
3. std::call_once 与 std::once_flag
class Singleton {
public:
static Singleton& getInstance() {
std::call_once(flag, [](){ instance.reset(new Singleton()); });
return *instance;
}
private:
Singleton() = default;
~Singleton() = default;
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
static std::unique_ptr <Singleton> instance;
static std::once_flag flag;
};
std::unique_ptr <Singleton> Singleton::instance;
std::once_flag Singleton::flag;
- 优点:显式控制初始化时机,适合在复杂初始化过程中使用。
- 缺点:相对冗长,但同样安全。
4. 静态成员指针 + 析构函数(延迟销毁)
如果你需要在程序结束时手动销毁单例,可以使用静态指针和自定义析构函数:
class Singleton {
public:
static Singleton* getInstance() {
if (!instance) {
instance = new Singleton();
std::atexit(destroyInstance);
}
return instance;
}
private:
Singleton() = default;
~Singleton() = default;
static void destroyInstance() { delete instance; instance = nullptr; }
static Singleton* instance;
};
Singleton* Singleton::instance = nullptr;
5. 对比总结
| 方法 | 线程安全性 | 初始化时机 | 代码简洁度 | 适用场景 |
|---|---|---|---|---|
| 局部静态变量 | C++11 以上保证 | 程序第一次调用 | 最高 | 适合大多数情况 |
| 双重检查锁 | 需要手动实现 | 程序第一次调用 | 中等 | 对性能有极致要求 |
call_once |
C++11 提供 | 程序第一次调用 | 中等 | 需要手动控制初始化 |
静态指针 + atexit |
需要手动锁 | 程序第一次调用 | 中等 | 需要手动销毁 |
小结
- 对于绝大多数 C++ 项目,局部静态变量已足够安全且简洁。
- 当你需要更细粒度的控制(如延迟销毁或复杂初始化),可以考虑
std::call_once或atexit方案。 - 记得总是禁止拷贝构造和赋值操作,确保单例唯一性。