设计模式里最常用到的一个模式就是单例了,当看到::getApp()
或者::getInstance()
这样的代码,基本就可以猜测这代码用了单例模式。
单例模式的好处就是可以保证某个类的对象全局只有一个,实现方面比较推崇OGRE的写法:
template <typename T>
class Singleton
{
protected:
static T* ms_Singleton;
public:
Singleton( void )
{
assert( !ms_Singleton );
ms_Singleton = static_cast< T* >(this);
}
~Singleton( void )
{ assert( ms_Singleton ); ms_Singleton = 0; }
static T& getSingleton( void )
{ assert( ms_Singleton ); return ( *ms_Singleton ); }
static T* getSingletonPtr( void )
{ return ms_Singleton; }
};
这样任何类只要想单例化,只需要从该类继承一下即可:
Class XXManager : public Singleton<XXManager>
{
...
};
当然记得要在cpp文件里初始化静态成员:
template<> XXManager* Singleton<XXManager>::ms_Singleton = 0;
而且更严谨一点,拷贝构造得在private下显示地写一下,防止拷贝构造产生第二个对象。
上面的写法,通过assert确保代码编写者,一定要先创建该类,才能使用,而且只能创建一次,好处是动态new,或者直接创建,都完全由程序员控制。 另一种常见的单例写法,是这样的:
class Engine
{
public:
static Engine* getapp()
{
if (!app_) {
app_ = new Engine();
}
return app_;
}
static void delapp()
{
if (app_) {
delete app_;
app_ = NULL;
}
}
void run();
private:
Engine();
//拷贝构造
Engine(const Engine& engine) {}
~Engine();
private:
static Engine* app_;
};
这种写法的好处是,不允许直接构造,用的时候,自动会构造一个出来。但这种写法的麻烦之处也很明显,构造是不用了,但是析构却是必须的了,用完了一定要调用delapp()来释放内存,当然啦,通常单例类都是全局唯一且存活时间跟服务运行时间一样长,程序都退出,内存释放与否都不那么重要了。尽管QA不会因为你程序退出时有内存没释放而找你麻烦,但是有必要养成良好的编程习惯。
上面的实现不是线程安全的,下面谈到本文重点了,就是多线程下,如何实现一个安全且高效的getapp(),方法是加锁的同时进行两次判断:
Singleton* Singleton::getInstance()
{
if(m_instance) {
return m_instance;
}
Lock();
if(!m_instance) {
m_instance = new Singleton();
}
UnLock();
return m_instance;
}
第一个判断是提高性能,因为一旦创建后,第一个判断就返回了。锁里的第二次判断是为了安全,确保只会new一次。像下面这样写,意思一样,但好像更漂亮些 。
Singleton* Singleton::getInstance()
{
if(NULL == m_instance) {
Lock();
if(NULL == m_instance) {
m_instance = new Singleton();
}
UnLock();
}
return m_instance;
}
不过总的来说,我比较推崇OGRE的写法,实现比较精妙,用起来会相当方便,而且做好初始化,程序跑的时候心里才踏实。设计模式是一些可借鉴的编程思想,而不是一定要坚守的条条框框,假使你约定所有名字以Manager结尾的类都只需要创建一个,我觉得也是很好的单例模式,代码易于阅读且性能高效就OK啦!