一、在C++中实现单例模式
例子工程的名称是Singleton。
1、模拟C#的方法实现单例模式
本人开始学习设计模式也是先看的C#语言描述的,后来要在C++上实现,参考了网上相关文章,使用一个静态类成员的指针来实现单例模式,代码如下:
SingletonSimulateGC类定义
class SingletonSimulateGC
{
private:
SingletonSimulateGC(void);
public:
static SingletonSimulateGC* GetInstance();
~SingletonSimulateGC(void);
public:
int SomeFunction();
private:
static SingletonSimulateGC *m_pInstance;
};
类实现如下
#include "SingletonSimulateGC.h"
SingletonSimulateGC *SingletonSimulateGC::m_pInstance = NULL;
SingletonSimulateGC* SingletonSimulateGC::GetInstance()
{
if (m_pInstance == NULL)
{
try
{
m_pInstance = new SingletonSimulateGC();
}
catch (...) //防止new分配内存可能出错的问题,如果是内存分配错误异常为std::bad_alloc
{
m_pInstance = NULL;
}
}
return m_pInstance;
}
SingletonSimulateGC::SingletonSimulateGC(void)
{
}
SingletonSimulateGC::~SingletonSimulateGC(void)
{
}
int SingletonSimulateGC::SomeFunction()
{
return 10;
}
代码很简单,很像C#的实现,使用静态成员方法GetInstance即可获得一个单例类的实例,这种写法在有垃圾回收机制的语言中是没有问题的,但是C++不行,如果仔细调试的话,会发现直到程序退出SingletonSimulateGC的析构方法都没有被调用过,对于大多数只保存数据的单例类来说,这没有什么,当程序退出的时候,进程会自动释放这些存储空间,但是如果是个设备管理单例类,在这个单例类的析构方法中管理着设备的关闭等清理方法,这个析构方法不被调用,只是进程退出的时候简单的执行一个类似CloseHandle方法,设备是会出现问题的,所以这种做法实际上是错误的。
为了解决这个错误问题,网上有一种做法如下
class CSingleton
{
// 其它成员
public:
static CSingleton * GetInstance()
private:
CSingleton(){};
static CSingleton * m_pInstance;
class CGarbo // 它的唯一工作就是在析构函数中删除CSingleton的实例
{
public:
~CGarbo()
{
if (CSingleton::m_pInstance)
delete CSingleton::m_pInstance;
}
};
static CGarbo Garbo; // 定义一个静态成员,在程序结束时,系统会调用它的析构函数
};
定义一个内嵌类来辅助释放单例类的单例实例,这个方法是正确的,但是其实现还没有下面第三种做法简单,实现的效果甚至比下面第三种做法还要差,首先单例类CSingleton对象实例可以延迟创建,但是CGarbo实例是系统启动时创建的,多了一个对象,而且这个类实例不能延迟创建,释放也是在系统退出的时候释放的,并不能在功能模块切换时将单例类实例释放掉,即不能解决本系列博文一开始提到的问题。
2、使用类静态成员实现单例模式
SingletonUseStaticClassMember类定义
class SingletonUseStaticClassMember
{
private:
SingletonUseStaticClassMember(void);
public:
static SingletonUseStaticClassMember* GetInstance();
~SingletonUseStaticClassMember(void);
public:
int SomeFunction();
private:
static SingletonUseStaticClassMember m_oInstance;
int m_i;
};
类实现如下
#include "SingletonUseStaticClassMember.h"
SingletonUseStaticClassMember SingletonUseStaticClassMember::m_oInstance;
SingletonUseStaticClassMember* SingletonUseStaticClassMember::GetInstance()
{
return &m_oInstance;
}
SingletonUseStaticClassMember::SingletonUseStaticClassMember(void)
{
}
SingletonUseStaticClassMember::~SingletonUseStaticClassMember(void)
{
m_i = 10;
}
int SingletonUseStaticClassMember::SomeFunction()
{
return 10;
}
在单例类中定义一个本类的静态成员,这种方式实例类的析构函数会被调用,一切都是正确的,但是这种方式很不好,不能够延迟创建实例类,实际上相当于系统定义了一个全局的类变量,生存期同应用程序一样,从这个角度看甚至还不如第一种做法。
3、使用函数的静态变量实现单例模式
SingletonUseStaticFunctionVariable类定义
class SingletonUseStaticFunctionVariable
{
private:
SingletonUseStaticFunctionVariable(void);
public:
static SingletonUseStaticFunctionVariable* GetInstance();
~SingletonUseStaticFunctionVariable(void);
int m_i;
public:
int SomeFunction();
};
类实现如下
#include "SingletonUseStaticFunctionVariable.h"
SingletonUseStaticFunctionVariable* SingletonUseStaticFunctionVariable::GetInstance()
{
static SingletonUseStaticFunctionVariable oInstance;
return &oInstance;
}
SingletonUseStaticFunctionVariable::SingletonUseStaticFunctionVariable(void)
{
m_i = 5;
}
SingletonUseStaticFunctionVariable::~SingletonUseStaticFunctionVariable(void)
{
m_i = 10;
}
int SingletonUseStaticFunctionVariable::SomeFunction()
{
return 10;
}
类定义里不包含类的实例变量或者指针,只在GetInstance方法中使用
static SingletonUseStaticFunctionVariable oInstance;
方法定义一个实例类的静态变量,多次调用GetInstance方法得到的都是同一个实例,这样就实现了单例,可以做到延迟创建,只有第一次调用GetInstance方法时才创建,并且单例类的析构方法在应用退出时可以被正确调用,这个是C++中实现单例模式比较好的方法。
4、使用单例类的例子
代码如下
#include "stdafx.h"
//#include "./Singleton/SingletonSimulateGC.h"
//#include "./Singleton/SingletonUseStaticClassMember.h"
#include "./Singleton/SingletonUseStaticFunctionVariable.h"
#include <iostream>
#include <ostream>
using namespace std;
int _tmain(int argc, _TCHAR* argv[])
{
char c;
int i;
{
//SingletonSimulateGC *pSingleton = SingletonSimulateGC::GetInstance();
//SingletonUseStaticClassMember *pSingleton =
// SingletonUseStaticClassMember::GetInstance();
SingletonUseStaticFunctionVariable *pSingleton =
SingletonUseStaticFunctionVariable::GetInstance();
i = pSingleton->SomeFunction();
}
cout << i << endl;
cin >> c;
return 0;
}
运行结果很简单,就输出一个10,是SomeFunction的返回结果,这里使用大括号控制生命周期,如果你在_tmain的
return 0;
和SingletonUseStaticFunctionVariable类的析构方法
m_i = 10;
上都加上断点,你会发现,SingletonUseStaticFunctionVariable的实例指针pSingleton退出大括号的范围并没有释放,先执行_tmain的return语句,后执行的SingletonUseStaticFunctionVariable类的析构方法,也就是单例类的释放总是在程序退出时才被调用。