版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u010021282/article/details/54234562
单例模式是23种设计模式中最简单,同时也是最常使用到的一种设计模式,它的特点是一个对象只会有一个实例被创建。
比如,在实际程序开发中,程序启动往往需要把配置文件的信息加载到一个类里面。我们希望这个类不被多次实例化,且能够被全局访问,这时候就可以将这个类设计成单例。
一个单例类,应当拥有以下成员:
1、一个静态私有变量或指针,指向自身
2、一个静态公有方法,用于获取类的唯一实例
3、私有的构造函数、拷贝构造函数、类赋值函数,保证不会在类外实例化
先来看看饿汉式和懒汉式的基本实现:
饿汉式:
class CSingleton
{
public:
virtual ~CSingleton(void){};
public:
static CSingleton* GetInstance();
private:
CSingleton(void){};
CSingleton(const CSingleton&);//不需要实现
CSingleton operator = (const CSingleton&);//不需要实现
private:
static CSingleton m_objInstance; //唯一实例
};
CSingleton CSingleton::m_objInstance;
CSingleton* CSingleton::GetInstance()
{
return &m_objInstance;
}
懒汉式:
class CSingleton
{
public:
virtual ~CSingleton(void){};
public:
static CSingleton* GetInstance();
private:
CSingleton(void){};
CSingleton(const CSingleton&);//不需要实现
CSingleton operator = (const CSingleton&);//不需要实现
private:
static CSingleton* m_pInstance;//唯一实例
};
CSingleton* CSingleton::m_pInstance=NULL;
CSingleton* CSingleton::GetInstance()
{
if (m_pInstance==NULL)
{
m_pInstance=new CSingleton();
}
return m_pInstance;
}
饿汉式跟懒汉式的区别就在实例化的时机不一样,懒汉式在第一次使用GetInstance获取实例的时候实例化,而饿汉式一开始就创建实例。
内存泄漏问题:
C++没有垃圾回收机制,若使用上面的饿汉式单例,由于m_pInstance得不到释放,将会造成内存泄漏!
这里我打算使用的智能指针share_ptr自动管理实例的内存,当程序结束时,自动释放m_pInstance:
#include "memory" //share_ptr头文件
class CSingleton;
typedef std::shared_ptr<CSingleton> Singleton_ptr;
class CSingleton
{
public:
virtual ~CSingleton(void) {};
public:
static CSingleton* GetInstance();
private:
CSingleton(void) {};
CSingleton(const CSingleton&);//不需要实现
CSingleton operator = (const CSingleton&);//不需要实现
private:
static Singleton_ptr m_pInstance;//唯一实例
};
Singleton_ptr CSingleton::m_pInstance;
CSingleton* CSingleton::GetInstance()
{
if (m_pInstance == NULL)
{
m_pInstance = Singleton_ptr(new CSingleton());
}
return m_pInstance.get();
}
当程序退出时,如果已经实例化,智能指针自动释放内存。
线程安全考虑:
内存泄漏的问题解决了,但是当多线程同时获取实例,还是可能会出现不可预期的异常,也就是线程安全问题仍未解决。
如果要保证线程安全,就必须上锁:
#include <mutex>
#include <memory>
class CSingleton;
typedef std::shared_ptr<CSingleton> Singleton_ptr;
class CSingleton
{
public:
virtual ~CSingleton(void) {};
public:
static CSingleton* GetInstance();
private:
CSingleton(void) {};
CSingleton(const CSingleton&);//不需要实现
CSingleton operator = (const CSingleton&);//不需要实现
private:
static Singleton_ptr m_pInstance;//唯一实例
static std::mutex m_mutex;
};
Singleton_ptr CSingleton::m_pInstance;
std::mutex CSingleton::m_mutex;
CSingleton* CSingleton::GetInstance()
{
if (m_pInstance == NULL)//1 判断指针是否为空
{
std::unique_lock<std::mutex> lk(m_mutex);//此处上锁
if (m_pInstance == NULL)//2 再次判断
{
m_pInstance = Singleton_ptr(new CSingleton());
}
}
return m_pInstance.get();
}
里面对m_pInstance作了两次判断,两次都是必要的:
若去掉判断1,程序不会出问题,但是效率会下降,因为每次获取实例都会加锁。
若去掉判断2,则有可能会出现被多次实例的可能,那就失去上锁的意义了。