转载:https://blog.csdn.net/k346k346/article/details/60142660
1、 概述
使用设计模式可以提高代码的可复用性、可扩充性和可维护性。单例模式(Singleton Pattern),确保一个类只有一个实例,并提供一个全局访问点。
有一些类对象我们只需要一个,比方说线程池(threadpool)、缓存(cache)、对话框、注册表(registry)、日志对象,充当打印机、显卡等设备的驱动程序的对象。此时,可以使用单例模式。
单例模式类图结构:
2、简单实现
(1)急切式
class Singleton
{
private:
Singleton() {};//构造函数私有
static Singleton pInstance;
public:
static Singleton* getInstance()
{
return &pInstance;
}
};
Singleton Singleton::pInstance;//定义静态类对象
这种方式在进入main函数前就完成了类对象的定义,避免了多线程的同步问题,但是没有做到需要类对象时才定义,没有达到“懒惰实例化”的效果。
(2)懒惰式(非线程安全)
class Singleton{
private:
Singleton(){} //构造函数私有
static Singleton* pInstance;
public:
static Singleton* getInstance(){
if (pInstance == NULL) //判断是否第一次调用
pInstance = new Singleton();
return pInstance;
}
};
Singleton* Singleton::pInstance=NULL;
这种方式延迟了类对象的实例化,在需要的时候再实例化。但上面因为没有线程同步操作,所以存在多线程不安全隐患,多个线程同时调用getInstance()时可能会创建多个类对象。
(3)懒惰式(线程安全)
HANDLE hMutex = CreateMutex(
NULL,//默认安全级别
FALSE,//创建它的线程不拥有互斥对象
NULL//没有名字
);
class Singleton
{
private:
Singleton() {}
static Singleton* pInstance;
public:
static Singleton* getInstance()
{
WaitForSingleObject(hMutex, INFINITE);//加锁
if (pInstance == NULL)
pInstance = new Singleton();
ReleaseMutex(hMutex);//释放锁
return pInstance;
}
};
Singleton* Singleton::pInstance = NULL;
(4)双重检查加锁式(Double-checked Locking,DCL)
HANDLE hMutex;
hMutex = CreateMutex( NULL,FALSE,NULL);
class Singleton{
private:
Singleton(){} //构造函数私有
static Singleton* pInstance;
public:
static Singleton* getInstance(){
if (pInstance == NULL){ //判断是否第一次调用
WaitForSingleObject(hMutex, INFINITE); //上锁
if (pInstance == NULL)
pInstance = new Singleton();
ReleaseMutex(hMutex); //解锁
}
return pInstance;
}
};
Singleton* Singleton::pInstance=NULL;
这种写法在getInstance()方法中进行了两次判空,第一次为了不必要的同步,第二次是在pInstance等于NULL的情况下才创建实例,同步操作实际上只发生了一次,大大提高了效率。
(5)局部静态变量式
class Singleton
{
private:
Singleton() {}
public:
static Singleton* getInstance()
{
static Singleton instance;
return &instance;
}
};
使用局部静态变量,非常巧妙的方法,完全实现了单例的特性,而且代码简洁优雅。
这里要注意一个问题,如果getInstance()函数返回的是类对象引用,会出现类拷贝的问题,这就违背了单例的特性。产生这个问题原因在于:编译器会为类生成一个默认的拷贝构造函数。例如下面的代码就会有问题:
Singleton singleton = Singleton::getInstance();//getInstance()返回对象引用
解决办法将默认拷贝构造函数申明为私有。
class Singleton{
private:
Singleton(){} //构造函数私有
Singleton(const Singleton&)=delete; //禁止拷贝构造函数
public:
static Singleton& getInstance(){
static Singleton instance;
return instance;
}
};
3、小结
(1)单例模式,确保一个类只有一个实例,并提供一个全局访问点。
(2)在确保程序中某个类只有一个实例时,请采用单例模式,且推荐局部静态变量式写法。