C++ 单例模式实现过程的演变
版本1:
class Singleton
{
public:
static Singleton*GetInstance()
{
if(_instance == nullptr)
{
_instance = new Singleton();
atexit(Destructor);
}
return _instance;
};
~Singleton(){};
private:
static void Destructor() {
if (nullptr != _instance) { //
delete _instance;
_instance = nullptr;
}
}
Singleton();//构造
Singleton(const Singleton &cpy); //拷⻉构造
Singleton& operator=(const Singleton& other) {}
static Singleton * _instance;
}
Singleton* Singleton::_instance = nullptr;//静态成员需要初始化
显而易见我们的单例模式存在线程安全的问题
版本2
class Singleton
{
public:
static Singleton*GetInstance()
{
std::lock_guard<std::mutex> lock(_mutex);
if(_instance == nullptr)
{
//std::lock_guard<std::mutex> lock(_mutex);//2.1错误
_instance = new Singleton();
atexit(Destructor);
}
return _instance;
};
~Singleton(){};
private:
static void Destructor() {
if (nullptr != _instance) { //
delete _instance;
_instance = nullptr;
}
}
Singleton();//构造
Singleton(const Singleton &cpy); //拷⻉构造
Singleton& operator=(const Singleton& other) {}
static Singleton * _instance;
static std::mutex _mutex;
}
Singleton* Singleton::_instance = nullptr;//静态成员需要初始化
std::mutex Singleton::_mutex; //互斥锁初始化
在这个版本中我们引用了互斥锁,并且是lock_guard的可以自动释放的互斥锁,但是这里会存在一个性能问题,就是对于任何想要获得单例对象的线程我们都会进行加锁,但是同时只会有一个线程获得,造成大量线程堵塞,然后如果我们将互斥锁的代码写在2.1处,我们会发现这样写还是会有线程不安全的问题
版本3
class Singleton
{
public:
static Singleton*GetInstance()
{
if(_instance == nullptr)
{
std::lock_guard<std::mutex> lock(_mutex);
if(_instance ==nullptr)
{
_instance = new Singleton();
atexit(Destructor);
}
}
return _instance;
};
~Singleton(){};
private:
static void Destructor() {
if (nullptr != _instance) { //
delete _instance;
_instance = nullptr;
}
}
Singleton();//构造
Singleton(const Singleton &cpy); //拷⻉构造
Singleton& operator=(const Singleton& other) {}
static Singleton * _instance;
static std::mutex _mutex;
}
Singleton* Singleton::_instance = nullptr;//静态成员需要初始化
std::mutex Singleton::_mutex;
这个版本的单例模式虽然可以有效的避免多线程之间的问题,同时效率也是挺高的,但是在new的过程中由于cpu reorder的操作,new的执行顺序可能不是按 1. 分配内存 2. 调用构造函数 3. 返回指针 这样的顺序来执行的,有可能是先返回的指针然后调用的构造函数,这样的操作会让多线程的单例模式变得不安全
版本四
class Singleton
{
public:
static Singleton& GetInstance() {
static Singleton instance;
return instance;
}
private:
Singleton(){}
~Singleton() {}
Singleton(const Singleton&) {}
Singleton& operator=(const Singleton&) {}
};
利用静态成员变量在c++11 magic static 特性:如果当变量在初始化的时候,并发同时进⼊声明语句,并发线程将会阻塞等待初始化结束。这样可以确保单例模式只会被调用一次,该初始化方式有四大优点1. 利⽤静态局部变量特性,延迟加载;2. 利⽤静态局部变量特性,系统⾃动回收内存,⾃动调⽤析构函数;3. 静态局部变量初始化时,没有 new 操作带来的cpu指令reorder操作;4. c++11 静态局部变量初始化时,具备线程安全;
版本5- 在版本四的基础上声明模板类
template<typename T>
class Singleton {
public:
static T& GetInstance() {
static T instance; // 这⾥要初始化DesignPattern,需要调⽤DesignPattern构造函数,同时会调⽤⽗类的构造函数。
return instance;
}
protected:
virtual ~Singleton() {}
Singleton() {} // protected修饰构造函数,才能让别⼈继承
Singleton(const Singleton&) {}
Singleton& operator =(const Singleton&) {}
};
class DesignPattern : public Singleton<DesignPattern> {
friend class Singleton<DesignPattern>; // friend 能让Singleton<T> 访问到DesignPattern构造函数
public:
~DesignPattern() {}
private:
DesignPattern() {}
DesignPattern(const DesignPattern&) {}
DesignPattern& operator=(const DesignPattern&) {}
};