单例模式
单例一般应用于全局中只需要一个该对像的情况, 自己创建自己并提供了该类的唯一访问接口。
一.懒汉式实现,
(1)该模式下选哟注意线程安全问题。使双检锁(DCLP)。
class SingletonLazy
{
private:
SingletonLazy(){};
static SingletonLazy* m_instance;
static std::mutex m_mutex;
SingletonLazy(const SingletonLazy&); //防止拷贝构造
SingletonLazy& operator = (const SingletonLazy&); //防止赋值运算符拷贝对象
~SingletonLazy(){}; //防止外界delete;
public:
//手动删除单例
static void DestroySingleton()
{
delete m_instance;
}
static SingletonLazy* Get_instance()
{
if(m_instance == nullptr) //双重判断提高效率,避免每次调用Get_instance都进行一次加解锁
{
std::lock_guard<std::mutex> lgard(m_mutex);
if(m_instance == nullptr)
{
m_instance = new SingletonLazy();
}
}
return m_instance;
}
};
SingletonLazy* SingletonLazy::m_instance = NULL;
std::mutex SingletonLazy:: m_mutex;
注意:由于编译器的实现不同,双检锁有时会失效。 例如:SingletonLazy = new SingletonLazy(), 在编译时这句代码分为三步
1 分配内存空间以保存单例对象。
2 在分配的内存中构造一个单例对象。
3 使m_instance指向分配的内存。
第二步和第三步顺序可能颠倒。 如果一个线程调用Get_instance该函数,代码走到m_instance = new SingletonLazy()这步时, 先给m_instance指向分配的内存,而没有进行对象的构造。此时又有一个线程访问Get_insatnce函数,此时m_instance已经不是空的了,返回实例给对方,由于此时实例没有构造出来,就会出现问题。
C++11给出了双检锁失效的解决办法, 使用原子操作
class SingletonLazy
{
private:
SingletonLazy(){};
static std::atomic<SingletonLazy*> m_instance;
static std::mutex m_mutex;
SingletonLazy(const SingletonLazy&); //防止拷贝构造
SingletonLazy& operator = (const SingletonLazy&);//防止赋值运算符拷贝对象
~SingletonLazy(); //防止外界delete;
public:
//手动删除单例
static void DestroySingleton()
{
delete m_instance;
}
static SingletonLazy* Get_instance()
{
SingletonLazy* tmp = m_instance.load(std::memory_order_relaxed);
std::atomic_thread_fence(std::memory_order_acquire); //获取屏障
if(tmp == nullptr)
{
std::lock_guard<std::mutex> lgard(m_mutex);
tmp = m_instance.load(std::memory_order_relaxed);
if(tmp == nullptr)
{
tmp = new SingletonLazy();
std::atomic_thread_fence(std::memory_order_relaxed); //释放屏障
m_instance.store(tmp, std::memory_order_relaxed);
}
}
return tmp;
}
};
std::atomic<SingletonLazy*> SingletonLazy::m_instance = NULL;
std::mutex SingletonLazy:: m_mutex;
(2)还有一种懒汉式实现,是使用局部静态变量的方法
class SingletonLazy
{
private:
SingletonLazy(){};
SingletonLazy(const SingletonLazy&); //防止拷贝构造
SingletonLazy& operator = (const SingletonLazy&);//防止赋值运算符拷贝对象
public:
~SingletonLazy() {};
static SingletonLazy* Get_instance()
{
static SingletonLazy instance;
return &instance;
}
}
这种方法叫做 Meyers’ SingletonMeyer’s的单例, 《Effective C++》系列书籍的作者 Meyers 提出的。当变量在初始化时 ,并发进入声明语句时,并发线程将会阻塞,等待初始化结束。由于是静态变量,只初始化一次,声明时又线程安全。所以不必加锁。推荐懒汉式这样实现
(3)单例模式也可以用饿汉式实现
class SingletonLazy
{
private:
SingletonLazy();
static SingletonLazy m_instance;
SingletonLazy(const SingletonLazy7); //防止拷贝构造
SingletonLazy& operator = (const SingletonLazy&); //防止赋值运算符拷贝对象
~SingletonLazy(); //防止外界delete;
public:
static SingletonLazy* Get_instance()
{
return &m_instance;
}
}
SingletonLazy SingletonLazy::m_instance;
该模式下程序一开始对象就生成了,如果一直没有用到,就会造成一定的内存浪费。