单件模式应该是所有设计模式中最简单的一个了,但是如果使用不当会出现很多问题,下面我们就分析一下单件模式
作用:保证一个class只有一个实体(instance),并为它提供一个全局唯一的访问点
适用性:
1、对于一个类(对象),如果它比较大(占用的系统资源非常多),而且这些资源可以被全局共享,则可以设计为singleton模式。
2、对于一个类,需要对实例进行计数。可以在Instance中进行,并可以对实例的个数进行限制。
3、对于一个类,需要对其实例的具体行为进行控制,例如,期望返回的实例实际上是自己子类的实例。这样可以通过Singleton模式,对客户端代码保持透明。
首先看一下单件模式的通用写法(注意可能会存在问题的,就看你怎么使用了)
//设计模式之单件模式
/*
作用:保证一个class只有一个实体(instance),并为它提供一个全局唯一的访问点
*/
class singleton
{
public:
~singleton()
{//析构时使用
}
static singleton* getInstance()
{
if(_instance == NULL)
{
_instance = new singleton();
}
return _instance;
}
private:
static singleton *_instance;
private:
//最好将所有此类的实例化的进口全部堵死
singleton()
{
}
singleton(const singleton&)
{
}
singleton& operator=(const singleton &)
{
}
};
singleton *singleton::_instance = NULL;
使用时,只需要调用singleton::getInstance()既可以获取到singleton的指针使用了,但是需要注意的一点是使用完成后需要调用delete singleton::getInstance();以便释放资源。
我们来分析一下上面的代码:
1、需要手动释放_instance,尤其是做接口时,需要告知使用方调用delete singleton::getInstance();语句。
2、如果在多线程环境下使用,问题更大了,如果大量线程调用到new时,可能会造成内存泄露,并且有可能前后获取的singleton对象不一致。
分析出了上面代码的问题,那我们应如何解决他们?
针对问题1:释放
1、调用delete singleton::getInstance();
2、注册atexit()函数,将释放内存的方法放入到atexit函数中,此种方法可以将多个单件放在一起调用。
void releasefun()
{
delete singleton::getInstance();
}
//在使用完成后调用
atexit(releasefun);
3、使用智能指针,比如STL的auto_ptr,于是我们的singleton变成了:
//设计模式之单件模式
/*
作用:保证一个class只有一个实体(instance),并为它提供一个全局唯一的访问点
*/
#include <memory>
#include <iostream>
using namespace std;
class singleton
{
public:
~singleton()
{//析构时使用
}
static singleton* getInstance()
{
if(NULL == _instance.get())
{
_instance.reset(new singleton);
}
return _instance.get();
}
private:
static auto_ptr<singleton> _instance;
private:
//最好将所有此类的实例化的进口全部堵死
singleton()
{
}
singleton(const singleton&)
{
}
singleton& operator=(const singleton &)
{
}
};
auto_ptr<singleton> singleton::_instance;
4、利用c++内嵌类和一个静态成员实现自动释放机制。
//设计模式之单件模式
/*
作用:保证一个class只有一个实体(instance),并为它提供一个全局唯一的访问点
*/
#include <memory>
#include <iostream>
using namespace std;
class singleton
{
public:
~singleton()
{//析构时使用
}
static singleton* getInstance()
{
if(_instance == NULL)
{
static clearer clr;
_instance = new singleton();
}
return _instance;
}
private:
static singleton *_instance;
private:
//最好将所有此类的实例化的进口全部堵死
singleton()
{
}
singleton(const singleton&)
{
}
singleton& operator=(const singleton &)
{
}
class clearer
{
public:
clearer(){}
~clearer()
{
if(singleton::getInstance())
{
delete singleton::getInstance();
}
}
};
};
singleton *singleton::_instance = NULL;
针对问题2:多线程版本
引入著名的双检测锁机制
static singleton* getInstance()
{
if(_instance == NULL)
{
//加入临界区
if(NULL == _instance)
{
_instance = new singleton();
}
//释放临界区
}
return _instance;
}
于是引入了我们的多线程版本:包含两种释放机制上例提到的3\4方法
//设计模式之单件模式
/*
作用:保证一个class只有一个实体(instance),并为它提供一个全局唯一的访问点
*/
#include <memory>
#include <iostream>
#include <windows.h>
using namespace std;
class lockguard
{
private:
CRITICAL_SECTION m_cs;
public:
lockguard()
{
InitializeCriticalSection(&m_cs);
}
~lockguard()
{
DeleteCriticalSection(&m_cs);
}
public:
class cguard
{
public:
cguard(lockguard &lg)
:m_lg(lg)
{
m_lg.guard();
}
~cguard()
{
m_lg.unguard();
}
private:
lockguard &m_lg;
};
private:
void guard()
{
EnterCriticalSection(&m_cs);
}
void unguard()
{
LeaveCriticalSection(&m_cs);
}
friend class lockguard::cguard;
};
class singleton
{
public:
~singleton()
{//析构时使用
}
private:
static lockguard _lg;
static singleton *_instance;
//static auto_ptr<singleton> _instance;
private:
//最好将所有此类的实例化的进口全部堵死
singleton()
{
}
singleton(const singleton&)
{
}
singleton& operator=(const singleton &)
{
}
class clearer
{
public:
clearer(){}
~clearer()
{
if(singleton::getInstance())
{
delete singleton::getInstance();
}
}
};
public:
static singleton* getInstance()
{
if(_instance == NULL)
{
lockguard::cguard gd(_lg);
if(NULL == _instance)
{
static clearer clr;
_instance = new singleton();
}
}
/*if(NULL == _instance.get())
{
_instance.reset(new singleton);
}*/
return _instance;
//return _instance.get();
}
};
singleton *singleton::_instance = NULL;
lockguard singleton::_lg;
//auto_ptr<singleton> singleton::_instance;
至此我们的singleton模式基本结束了,这个模式还是包含很多c++深层的知识的。