C++ 设计模式之单例模式
正确写法
该写法在第一次调用get_instance()
后构造该实例,线程安全。
class object{
public:
~object() = default;
static std::unique_ptr<object>& get_instance();
private:
object() = default; ///< 构造函数写为private,防止其他调用者单独构造该对象实例。
};
std::unique_ptr<object>& object::get_instance();
static std::unique_ptr<object> instance; ///< 该对象的唯一实例
static std::once_flag flag; ///< 标志位, 标记只调用一次
std::call_once(flag, [&](){
instance = std::make_unique<object>(); ///< C++14以后版本的方法
/*
instance = std::unique_ptr<object>(new object()); ///< C++14 到 C++11 可用的方法
*/
});
return instance;
}
其他写法比较
最简单的写法: 线程不安全
由于new object()
这个构造的过程需要时间,所以可能造成两个线程同时获取到instance
变量为空指针。从而导致实例化两次,从未导致硬件驱动加载两次,而导致崩溃。
object* object::get_instance() {
object* instance = nullptr;
if(instance == nullptr) {
instance = new object();
}
return instance;
}
double check
写法: 看起来线程安全,其实有条件竞争。
在#1
和#2
处,可能发生一个线程正在对instance
变量赋值(写操作), 而另一个线程在进行在进行判断instance
变量是否为空(读操作),从而导致条件竞争,而导致崩溃。
object* object::get_instance(){
static std::mutex mt;
volatile object* instance = nullptr; ///< volatile关键字为了防止编译器优化
if(instance == nullptr) { ///< #1 读操作
std::lock_guard<std::mutex> lock(mt);
if(instance == nullptr) {
instance = new object(); ///< #2 写操作
}
}
return instance;
}