题目
实现线程安全的单例模式并解释C++11的改进
信息
- 类型:问答
- 难度:⭐⭐
考点
单例模式, 线程安全, C++11静态初始化, 移动语义, 内存模型
快速回答
线程安全单例模式的C++11实现要点:
- 使用
局部静态变量实现(C++11保证其初始化线程安全) - 删除拷贝构造和赋值操作符
- 可选
std::call_once或原子操作实现 - 正确处理移动语义(C++11新增)
原理说明
C++11标准规定:函数内的静态局部变量初始化具有线程安全性,编译器会自动插入同步代码(类似原子操作+内存屏障)。这解决了传统双重检查锁定(DCLP)在旧标准中的线程安全问题。
代码示例
class Singleton {
public:
// 获取单例实例
static Singleton& getInstance() {
static Singleton instance; // C++11保证线程安全初始化
return instance;
}
// 删除拷贝和赋值
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
// 可选:显式删除移动操作(C++11新增)
Singleton(Singleton&&) = delete;
Singleton& operator=(Singleton&&) = delete;
private:
Singleton() = default; // 私有构造函数
~Singleton() = default;
};
// 使用示例
auto& instance = Singleton::getInstance();
最佳实践
- 首选静态局部变量:简洁安全,符合RAII原则
- 显式删除拷贝/移动操作:防止意外复制(C++11新特性)
- 内存顺序保证:C++11内存模型确保初始化对所有线程可见
- 延迟初始化:首次调用时构造,避免静态初始化顺序问题
常见错误
- 使用旧版双重检查锁定:
// 危险!C++11前DCLP存在数据竞争 if(!instance) { // 未同步的读操作 std::lock_guard<std::mutex> lock(mutex); if(!instance) instance = new Singleton(); } - 忽略移动语义:未删除移动操作可能导致资源转移
- 返回指针而非引用:增加内存管理复杂度,可能引发delete错误
扩展知识
- std::call_once 替代方案:
static std::once_flag flag; static Singleton* instance; std::call_once(flag, []{ instance = new Singleton(); }); - 内存模型:C++11引入的
std::memory_order为底层同步提供精细控制 - Meyer's Singleton:上述静态局部变量实现即为此模式,被公认为现代C++最佳实践
- 单例的适用场景:配置管理、日志系统等真正需要全局唯一对象的场景