三、使用模板类实现的通用单例类实例获取器
例子工程的名称是GeneralFreeInstanceGetter。
1、通用的模板类单例类实例获取器
功能同前文描述的单例类获取器一样,我们改成模板类方式,把单例类作为模板参数,以便变成通用的单例类实例获取器。
模板类的定义与实现
namespace dpex
{
template <typename T>
class SingletonInstanceGetter
{
public:
SingletonInstanceGetter(void)
{
if (m_pInstance == NULL)
{
try
{
m_pInstance = new T();
}
catch (...) //防止new分配内存可能出错的问题,如果是内存分配错误异常为std::bad_alloc
{
m_pInstance = NULL;
}
}
//这里不管SingletonExample创建成功与否都要加1
m_uiReference++;
}
~SingletonInstanceGetter(void)
{
m_uiReference--;
if (m_uiReference == 0)
{
if (m_pInstance != NULL)
{
delete m_pInstance;
m_pInstance = NULL; //非常重要,不然下次再次建立单例的对象的时候错误
}
}
}
public:
T *GetInstance()
{
return m_pInstance;
}
private:
static T *m_pInstance;
static unsigned int m_uiReference;
};
template <typename T>
T *SingletonInstanceGetter<T>::m_pInstance = NULL;
template <typename T>
unsigned int SingletonInstanceGetter<T>::m_uiReference = 0;
}
其实就是把前文的单例类实例获取器变成模板类,把单例类作为模板参数,具体的功能与实现同前文中的单例类实例获取器相同,这里就不再过多解释,如果有什么疑问可以回看前文。
2、使用通用单例类实例获取器的单例类
正如前文中描述的一样,单例类就是普通类做出2点小改变,使用通用模板类的单例类仍然如此简单,为了后边演示程序测试通用单例类实例获取器的通用性,我们定义了2个单例类SingletonExampleOne、SingletonExampleTwo,两个类代码基本一样,这里给出SingletonExampleOne的定义与实现。
类定义
#include "../SingletonInstanceGetter/SingletonInstanceGetter.h"
using dpex::SingletonInstanceGetter;
class SingletonExampleOne
{
//template <class T>
//friend class SingletonInstanceGetter;
//template <class T>
//friend SingletonInstanceGetter<T>::SingletonInstanceGetter();
friend SingletonInstanceGetter<SingletonExampleOne>::SingletonInstanceGetter();
private:
SingletonExampleOne(void);
public:
~SingletonExampleOne(void);
public:
int GetVariable();
void SetVariable(const int nValue);
private:
int m_nVariable;
};
类实现
#include "SingletonExampleOne.h"
SingletonExampleOne::SingletonExampleOne(void)
{
m_nVariable = 0;
}
SingletonExampleOne::~SingletonExampleOne(void)
{
}
int SingletonExampleOne::GetVariable()
{
return m_nVariable;
}
void SingletonExampleOne::SetVariable(const int nValue)
{
m_nVariable = nValue;
}
可以看到和普通类的区别就仅仅2点,一个是把构造函数变成private,然后使用友元函数的方式,将单例类变成通用单例类实例获取器构造函数的友元,方便这个构造函数可以调用单例类的私有成员,其实就是单例类的私有构造函数。
3、测试程序例子及结果
测试程序的代码如下
#include "./FreeSingleton/SingletonExampleOne.h"
#include "./FreeSingleton/SingletonExampleTwo.h"
#include "./SingletonInstanceGetter/SingletonInstanceGetter.h"
using std::cin;
using std::cout;
using std::endl;
int _tmain(int argc, _TCHAR* argv[])
{
int i;
{
SingletonInstanceGetter<SingletonExampleOne> sigSingletonInstanceGetter1;
SingletonExampleOne *pseSingleton1 = sigSingletonInstanceGetter1.GetInstance();
i = pseSingleton1->GetVariable();
cout << "类型一实例成员变量初始值:" << i << endl;
pseSingleton1->SetVariable(10);
i = pseSingleton1->GetVariable();
cout << "类型一改变实例成员变量以后的值:" << i << endl;
{
SingletonInstanceGetter<SingletonExampleOne> sigSingletonInstanceGetter2;
SingletonExampleOne *pseSingleton2 = sigSingletonInstanceGetter2.GetInstance();
i = pseSingleton2->GetVariable();
cout << "类型一不释放前一实例获取器的情况下使用新实例获取器获得的实例成员变量的值:" <<
i << endl;
SingletonExampleOne *pseSingleton3 = sigSingletonInstanceGetter2.GetInstance();
i = pseSingleton3->GetVariable();
cout << "类型一不释放前一实例获取器的情况下使用新实例获取器再次获得的实例成员变量值:"
<< i << endl;
}
}
{
SingletonInstanceGetter<SingletonExampleTwo> sigSingletonInstanceGetter1;
SingletonExampleTwo *pseSingleton1 = sigSingletonInstanceGetter1.GetInstance();
i = pseSingleton1->GetVariable();
cout << "类型二实例成员变量初始值:" << i << endl;
pseSingleton1->SetVariable(20);
i = pseSingleton1->GetVariable();
cout << "类型二改变实例成员变量以后的值:" << i << endl;
{
SingletonInstanceGetter<SingletonExampleTwo> sigSingletonInstanceGetter2;
SingletonExampleTwo *pseSingleton2 = sigSingletonInstanceGetter2.GetInstance();
i = pseSingleton2->GetVariable();
cout << "类型二不释放前一实例获取器的情况下使用新实例获取器获得的实例成员变量的值:" <<
i << endl;
SingletonExampleTwo *pseSingleton3 = sigSingletonInstanceGetter2.GetInstance();
i = pseSingleton3->GetVariable();
cout << "类型二不释放前一实例获取器的情况下使用新实例获取器再次获得的实例成员变量值:"
<< i << endl;
}
}
SingletonInstanceGetter<SingletonExampleOne> sigSingletonInstanceGetterOne3;
SingletonExampleOne *pseSingletonOne4 = sigSingletonInstanceGetterOne3.GetInstance();
i = pseSingletonOne4->GetVariable();
cout << "类型一释放了前边的实例获取器后,再次使用实例获取器获取实例成员变量值:" << i << endl;
SingletonInstanceGetter<SingletonExampleTwo> sigSingletonInstanceGetterTwo3;
SingletonExampleTwo *pseSingletonTwo4 = sigSingletonInstanceGetterTwo3.GetInstance();
i = pseSingletonTwo4->GetVariable();
cout << "类型二释放了前边的实例获取器后,再次使用实例获取器获取实例成员变量值:" << i << endl;
char c;
cin >> c;
return 0;
}
运行结果如下
类型一实例成员变量初始值:0
类型一改变实例成员变量以后的值:10
类型一不释放前一实例获取器的情况下使用新实例获取器获得的实例成员变量的值:10
类型一不释放前一实例获取器的情况下使用新实例获取器再次获得的实例成员变量值:10
类型二实例成员变量初始值:0
类型二改变实例成员变量以后的值:20
类型二不释放前一实例获取器的情况下使用新实例获取器获得的实例成员变量的值:20
类型二不释放前一实例获取器的情况下使用新实例获取器再次获得的实例成员变量值:20
类型一释放了前边的实例获取器后,再次使用实例获取器获取实例成员变量值:0
类型二释放了前边的实例获取器后,再次使用实例获取器获取实例成员变量值:0
从程序中可以看出,用一个单例类作为模板参数使用通用单例类实例获取器是很简单的,只需要简单的一句话就可以了,然后调用单例类实例获取器实例成员方法GetInstance就可以获得需要的单例类的单例实例
SingletonInstanceGetter<SingletonExampleOne> sigSingletonInstanceGetter1;
SingletonExampleOne *pseSingleton1 = sigSingletonInstanceGetter1.GetInstance();
具体的测试程序分为3部分,第一部分使用多个通用单例类实例获取器实例多次获取单例类SingletonExampleOne的单例实例,第二部分演示了使用多个通用单例类实例获取器实例多次获取单例类SingletonExampleTwo的单例实例,第三部分当前面2部分的单例类实例获取器对象都释放后,重新获取SingletonExampleOne和SingletonExampleTwo的实例,发现这次获得的都是新的实例,不再是前面已经获取的单例实例了,说明前面的单例实例伴随他们的单例类实例获取器实例一起释放掉了。
4、一些说明
从例子可以看出,当你第一次使用单例类获取器获得一个单例实例后,在这个单例类获取器实例生命周期内(没有释放这个单例类获取器的实例),无论再通过这种方式获取多少次单例类实例都是这个单例类的唯一实例,但是如果生存期最长的单例类获取器实例释放后,这个单例类的实例就会被释放,这也正是我们想要的按需释放单例类效果。
但是有个问题,那就是开发人员要很好的控制生存期最长的单例类获取器实例,如果控制不好就会引起一些问题
A、对于设备管理类的单例类,会出现频繁的开关设备,浪费很多的时间,还有可能引起设备的状态不正确;
B、对于有状态的单例类(例子中为了说明是单例就使用了有状态的单例类),如果控制不好,可能被释放后再访问就会获得不正确的状态,访问了初始化的状态值;
C、对于字典表类型的单例类,控制不好就会造成单例对象的频繁新建释放,影响系统性能。
经过以上分析,使用这种模型具有很多好处,其前提就是一定要能控制好单例类获取器实例的生命周期,主要是指外层生存期最长的那个单例类获取器实例,如果控制不好轻则影响性能,严重了可能引起错误。
初学者看到这里可能觉得那这不是太麻烦了,控制生命周期是不是很难,其实也很容易,可以放心使用,举个简单例子,人业务要使用民族的单例类Nation,那就在整个人业务过程中保持一个Nation的单例类实例获取器就可以了,即进入人业务的时候使用
SingletonInstanceGetter<Nation> sigNationInstanceGetter;
一条语句创建一个Nation的单例类获取器,然后保证sigNationInstanceGetter在整个人任务中不会释放就可以了,甚至都不用调用sigNationInstanceGetter的实例成员方法GetInstance,因为sigNationInstanceGetter构造时就把Nation的实例创建了,这样就能保证退出人业务的时候Nation单例类实例从内存中释放掉,在人业务中也不会频繁的创建释放这个单例类。