高效的单例模式

设计模式里最常用到的一个模式就是单例了,当看到::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啦!

发表于 2013年07月15日 14:32   评论:0   阅读:2272  



回到顶部

首页 | 关于我 | 关于本站 | 站内留言 | rss
python logo   django logo   tornado logo