这篇博客实现了一种自动的Singleton的设计模式的实现,相比起通常的Singleton的实现,这种方法更加简单而且更加灵活。

Singleton设计模式

单例模式在项目中是一种很重要的设计模式,它能保证一个对象在项目中只存在一个实例。在游戏中,例如说Texture Manager,Data Table这些东西应该都是从游戏开始时就创建一个实例,一直持续到游戏关闭。

Singleton往往被视为项目中的全局对象,因此相对于那些必须从属于某些事务的对象来说,全局对象的Singleton往往更加高效而且简便。否则,如果通过下面这种方式来获得一个TextureManager往往会让人崩溃:

GetApp()->GetServices()->GetGui()->GetTextureMgr();

当然,我们可以声明为外部连接全局范围的g_TextureMgr来访问。这样虽然的确很方便,但是由于全局对象的创建和销毁次序取决于执行时候的情况,对于那些可移植的项目,这些次序无法预计,因此效果还是不太好。

以往的方法

在教科书中,对于Singleton的管理通常如下:

TextureMgr& GetTextureMgr() 
{
    static T s_Singleton;
    return s_Singleton;
}

当然为了方便,有些项目里面使用了模版或者宏,但是本质都是一样——在第一次调用的时候进行实例化,然后在程序解释的时候进行销毁。

但是这种做法有一个缺点在于是它无法控制资源的销毁——也就是说它必须在程序结束的时候才能释放掉。换句话说,我们对于资源的把控程度还不够。如果说我们想要在游戏运行的时候关闭掉一部分的话,那么这种做法显然是无法实现的。

换言之,我们需要能够追踪到这个资源,那么也就是需要找到指向它的指针。

有这么一种方法:

class TextureMgr 
{ 
    static TextureMgr* ms_Singleton; 

public: 
    TextureMgr() 
    {
        ms_Singleton = this; 
        // ... 
    } 

    ~TextureMgr() 
    { 
        ms_Singleton = NULL; 
        // ...
    } 

    TextureMgr& GetSingleton() 
    {
        return (*ms_Singleton);
    } 
};

如果使用上面这种方法,就可以在任何时候创建以及销毁TextureMgr,而且也可以很方便地访问Singleton。

但是还有一点不方便,那就是用于追踪Singleton的代码需要加到每个类中,代码会显得比较累赘。

自动Singleton的实现

自动Singleton是使用模版来自动定义Singleton指针,并且完成指针设置、查询和清除的工作。它还可以使用assert来确保没有把Singleton实例化多次。

但是最重要的一点——只需要从这个类进行派生就可以获得Singleton的功能了

代码如下:

#include <cassert>
template < typename T>
class Singleton
{ 
    static T * ms_Singleton;

public : 
    Singleton () 
    { 
        assert (! ms_Singleton ); 
        int offset = (int )( T*) 1 - ( int )(Singleton < T>*)( T *)1 ;

        ms_Singleton = ( T *)( (int ) this + offset ); 
    } 
    ~Singleton () 
    { 
        assert ( ms_Singleton ); 
        ms_Singleton = NULL ; 
    } 

    static T & GetSingleton () 
    { 
        assert ( ms_Singleton ); 
        return (* ms_Singleton ); 
    } 
    static T * GetSingletonPtr () 
    { 
        return ms_Singleton; 
    } 
};

template < typename T >
T * Singleton < T>:: ms_Singleton = 0 ;

上面代码中比较容易造成疑惑的地方是构造函数中的offset逻辑。这个涉及到对象比较和类型转化的问题了,在另一篇博客中又提到,传送门

如果需要将任何类进行Singleton化,需要将其从Singleton进行公开继承即可。这种方法不会影响到类的大小,只会增加一些自动的函数调用。

当然,我们确保在使用前有对应的MyClass实例化。只要被实例化了,它就会被追踪。这样一来就可以使用MyClass::GetSingleton ()方法来获得这个对象。

<全文完>