声明:下面的文档为“史旭露”原创,可随意转载,其中的代码可随意使用。但作者不最代码中可能存在的问题负责,若源代码使用中出现的BUG对使用者造成损失,作者不负任何责任(邮箱:sxl19@126.com)
singleton模式是设计模式中最简单的模式,但也是使用做多的模式。
大多数情况下,我们使用singleton模式的时候,会采取如下方法:
class CSingle
{
CSingle(){};
static CSingle * _m_spIns;
public:
virtual ~CSingle(){};
};
CSingle* CSingle::_m_spIns = NULL;
上面的代码存在以下几点需要注意的地方:
1、将需要定为单件的类的构造函数写成私有
2、类中包含一个单件类类型的静态指针,并在类外部的全局代码区初始化为空。需要注意的是,由于_m_spIns被设定为私有,这里初始化的时候采用的是重新定义的方法来初始化,而静态对象只能初始化一次。所以这里相当于CSingle::_m_spIns = NULL;
当然,采用上面的做法将一个类变成单件类,改动并不大。但是我们可以采用更加方便且更加优雅的方式将这个类修改为单件类。下面给出方法:
先贴出代码:(以下代码在VS2005和VS2010中编译通过并能正确运行)
class T;template <class T> class CSingleton
{
public:
static T * GetInstance();
static void ReleaseInstance();
virtual ~CSingleton();
private:
CSingleton();
static T * _m_pIns;
};
template <class T> T* CSingleton<T>::_m_pIns = NULL;
template <class T>
T * CSingleton<T>::GetInstance()
{
if (NULL == _m_pIns)
_m_pIns = new T;
return _m_pIns;
};
template <class T>
void CSingleton<T>::ReleaseInstance()
{
if ( NULL != _m_pIns )
{
delete _m_pIns;
_m_pIns = NULL;
}
};
上面的CSingleton是个C++模板类,这个模板类比较有意思。其使用方法村子以下两种
1、植入模式
2、外套模式
首先,给出植入模式的使用方法。植入模式需要做以下工作:
1.1、让CEmbededMode 派生自CSingleton<CEmbededMode>
1.2、将构造函数改为私有
1.3、声明CSingleton<CEmbededMode>为友类
class CEmbededMode : public CSingleton<CEmbededMode>
{
friend class CSingleton<CEmbededMode>;
CEmbededMode(){
printf("CEmbededMode constructed\n");
};
public:
virtual ~CEmbededMode(){
printf("CEmbededMode destructed\n");
};
};
获取CEmbededMode 类的单件对象句柄,方法如下
CEmbededMode* pEIns1 = CEmbededMode::GetInstance();
植入模式的本质,实际是先通过声明CSingleton<CEmbededMode>来创建了一个包含CEmbededMode 类型静态指针的基类,这个过程是在模板编译时,由编译器来完成的。然后CEmbededMode 从CSingleton<CEmbededMode>派生,得到了自身类型的静态指针。注意,由于这里的指针是静态指针,所以在继承的时候没有对其父类的这个指针进行拷贝,而是与其无法实例化的父类共用了一份数据。(这一点有点绕,朋友们需要好好体会^_^)
其次,给出外套模式使用方法:
2.1、将构造函数改为私有
2.2、声明CSingleton<CEmbededMode>为友类
class CWrapperMode{
friend class CSingleton<CWrapperMode>;
CWrapperMode(){
printf("CWrapperMode constructed\n");
};
public:
virtual ~CWrapperMode(){
printf("CWrapperMode destructed\n");
};
};
获取CWrapperMode 类的单件对象句柄,方法如下
CWrapperMode* pWIns = CSingleton<CWrapperMode>::GetInstance();
外套模式的本质,实际上是通过模板编译器在编译器创建了CSingleton<CWrapperMode>类,然后通过这个类间接得到CWrapperMode实例句柄的方式。(这种模式相对容易理解一些)
单元测试代码如下:
#include "stdafx.h"
#include "SingletonTemplate.h"
#include "stdio.h"
class CEmbededMode : public CSingleton<CEmbededMode>
{
friend class CSingleton<CEmbededMode>;
CEmbededMode(){
printf("CEmbededMode constructed\n");
};
public:
virtual ~CEmbededMode(){
printf("CEmbededMode destructed\n");
};
};
class CWrapperMode
{
friend class CSingleton<CWrapperMode>;
CWrapperMode(){
printf("CWrapperMode constructed\n");
};
public:
virtual ~CWrapperMode(){
printf("CWrapperMode destructed\n");
};
};
int _tmain(int argc, _TCHAR* argv[])
{
//植入模式
printf("创建pEIns1、pEIns2、pEIns3\n");
CEmbededMode* pEIns1 = CEmbededMode::GetInstance();
CEmbededMode* pEIns2 = CEmbededMode::GetInstance();
CEmbededMode* pEIns3 = CEmbededMode::GetInstance();
printf("pEIns1=%x,pEIns2=%x,pEIns3=%x\n",pEIns1,pEIns2,pEIns3);
//外套模式
printf("创建pWIns1、pWIns2、pWIns3\n");
CWrapperMode* pWIns1 = CSingleton<CWrapperMode>::GetInstance();
CWrapperMode* pWIns2 = CSingleton<CWrapperMode>::GetInstance();
CWrapperMode* pWIns3 = CSingleton<CWrapperMode>::GetInstance();
printf("pWIns1=%x,pWIns2=%x,pWIns3=%x\n",pWIns1,pWIns2,pWIns3);
//释放资源
CEmbededMode::ReleaseInstance();
CSingleton<CWrapperMode>::ReleaseInstance();
return 0;
}
下面说说植入模式和外套模式各自的优缺点。
植入模式相对外套模式要不容易理解一些,而且使用也相对麻烦一些(与本文最初给出的单件模式的使用方法的麻烦程度不相上下)。但是这个模式有一个很大的好处!那就是在多线程环境下可以做出线程安全的单件基类。通过这个基类,我们很容易就可以得到各种线程安全的单件类。换句话说,这种模式提供了将线程安全的控制代码集成进来的可能性。而相比之下,外套模式则不具有这个能力,必须将控制线程安全的代码加入到各个被操纵的类里面,而无法统一在外套里面解决这个问题。当然,你可以使诸如CWrapperMode这样的类派生自一个控制线程安全的类,以图CWrapperMode也具备线程安全的能力。这一点,就看自己的喜好了。
小结:
单件模板类,由于使用的角度不同,从而造成了这个类的本质性的变化。这是个很有意思的模板类,以上也是自己在平常使用中的一点小小的心得。文中如有错误之处,希望大家多多指正