单例模式是一种非常常用的设计模式,几乎在稍大的工程项目中都会用到。单例类保证在全局中只有唯一一个类的实例对象,并且在类的内部提供了获取这个唯一实例的接口。在类中,实例化出一个对象需要调用构造函数,为了防止在类的外部调用构造函数构造出实例,对类的构造函数就应有所限制,可以将构造函数的访问权限设置为private或protected。还要提供一个访问实例的接口,在类中定义一个static函数,返回类内部唯一的实例对象即可。
---------------------
#include <mutex>
class Singleton
{
private:
Singleton() //构造函数声明为私有的
:_a(0)
{}
//防拷贝
Singleton(const Singleton&);
Singleton& operator=(const Singleton&);
public:
static Singleton* GetInstance() //声明一个静态类型的接口来获取实例
{
if (_instance== NULL)
{
lock_guard<mutex> lock(_mtx);
if (_instance== NULL)
{
_instance = new Singleton();
}
}
return _instance;
}
void DelInstance() //销毁实例
{
lock_guard<mutex> lock(_mtx);
if (_instance)
{
delete _instance;
_instance = NULL;
}
}
private:
int _a;
static Singleton* _instance;//声明一个静态成员作为类的实例
static mutex _mtx;//保证线程安全的互斥锁
};
Singleton* Singleton::_instance = NULL; //初始化静态成员变量
mutex Singleton::_mtx;
以上代码中加了互斥锁,保证了线程安全。但也不是绝对安全的,因为加锁或解锁会引入新的问题,有可能抛异常或者造成死锁。解决了线程安全,那么就可以进一步提高效率。在上述代码中,可以看到用了两次判断,也叫双检查机制,这就保证了只在第一次获取对象时加锁,避免高并发场景下每次获取实例对象都进行加锁,提高了效率。
---------------------
懒汉模式:在第一次调用GetInstance的时候才实例化出对象,此后返回的都是该对象。相对饿汉模式而言,复杂,要确保线程安全问题,但在各种场景下都适用。
饿汉模式:无论是否需要该类的实例,在程序一开始的时候会产生该类的实例对象,此后返回的都是该对象。由于是在main函数之前创建线程,可能会出现不确定问题,适用性受到限制。
上面代码实现的是懒汉模式,那么下边就实现饿汉模式
---------------------
namespace Hungry
{
class Singleton
{
private:
Singleton() //构造函数声明为私有的
:_a(0)
{}
//防拷贝
Singleton(const Singleton&);
Singleton& operator=(const Singleton&);
public:
//static Singleton& GetInstance()
//{
// assert(_instance);
// return *_instance;
//}
static Singleton& GetInstance() //在类内部创建静态实例,全局只此一份
{
static Singleton _instance;
return _instance;
}
void DelInstance() //销毁实例
{
if (_instance)
{
delete _instance;
_instance = NULL;
}
}
private:
int _a;
static Singleton* _instance;
};
Singleton* Singleton::_instance = NULL;
//Singleton* Singleton::_instance = new Singleton();//全局生成一个实例对象
}