• Ogre 设计模式之Singleton


    Ogre很多地方采用了单件模式,单件模式的好处在于维护某个类在程序中只有唯一的实例,实例可以在程序的命名空间中的如何地方被直接调用,这样就避免了对实例指针的传递。

    LogManager ,ControllerManager, DyLibManager等都是使用的单例模式。

    既然知道了单例模式的好处和Ogre的广泛用途,少年你肯定想知道Ogre的singleton是如果实现的吧。下面我们结合LogManager来对它研究一番。

    单例模式大家都知道(PS : 不知道的少年可以参考《设计模式》一书),我们下面主要针对普通Singleton和Ogre的Singleton的相同点和不同点来进行一下分析

    相同点:

     

    1           维护某个类在程序中只有唯一的实例,都可以通过getSingleton() , getSingletonPtr()或instance()等方式来获得唯一的实例。

    2           因为要用到不同的派生类中,一般把Singleton提出来用template加以实现,这样派生类直接继承Singleton,并实例化模板参数即可,如下:

    template <typename T> class Singleton
    
    class _OgreExport LogManager : public Singleton<LogManager>, public LogAlloc

    其中的LogAlloc,这里暂时不展开来谈,等到分析Ogre内存分配的时候,再详细的说明。对于继承于Singleton的类的具体实现,我们要特化Singleton的static成员变量,对于LogManager

    template<> LogManager* Singleton<LogManager>::msSingleton = 0; 

     

    不同点:

     1.         普通的Singleton如(boost Singleton)构造函数为protected,程序调用instance的地方生成实例,在需要的地方直接调用instance或者getSingleton这类的函数即可:

    // singleton.hpp
    
    template <class T>
    
    /*static*/ T &singleton<T>::instance()
    
    {
    
        // function-local static to force this to work correctly at static
    
        // initialization time.
    
        static singleton<T> s_oT;
    
        return(s_oT);
    
    }
    
     
    
    MyClass::instance().doSomething();

    而Ogre的Singleton有个特点在于它的构造函数是public的,初始化的而且是需要new的,如下:

    mLogManager = OGRE_NEW LogManager();
    mLogManager->createLog(logFileName, true, true);

    但在之后调用的时候就直接getSingleton就可以了。

           我认为Ogre这样做的原因,是把最耗时间的初始化操作放在程序的开始,而等到真实渲染的时候,不必再为初始化instance而耗费时间,这个设计的缺点在于初始化的时间耗费比较多。

    2.         Ogre使用单例比较特殊的地方,在于在每个单例里面,他都会重写,而重写的函数只调用基类Singleton的方法,如下:

    static LogManager& getSingleton(void);
    static LogManager* getSingletonPtr(void);

         这样做的好处在于把模板的操作限定在单个类的实现中,而编译器不需要编程时再跑到Singleton的头文件中,再对模板进行编译。当你如果dll中导出的单件时候,不是基于单个文件的模板类就会出问题,会出现链接错误的情况,经本人确认的确如此,如果在具体的单件中重写两个方法就会把static的实例化限制在单个编译单元中,而函数只需要调用基类的方法,这样可以避免link error。

    3.         因为Ogre跨平台和支持多编译器的特性,在Singleton 的构造函数都会对编译器的版本做一次判断,如下:

            Singleton( void )
    
            {
    
                assert( !msSingleton );
    
    #if defined( _MSC_VER ) && _MSC_VER < 1200    
    
                int offset = (int)(T*)1 - (int)(Singleton <T>*)(T*)1;
    
                msSingleton = (T*)((int)this + offset);
    
    #else
    
             msSingleton = static_cast< T* >( this );
    
    #endif
    
            }

           比较难理解的地方在于#if defined( _MSC_VER ) && _MSC_VER < 1200 这行的预编译判断,<1200 意味着 vc的版本小于 vc6.0 (囧 那么老的编译器还有人用吗) ,static的内存模型就会不同,需要做this指针做一次offset的调整,具体的内存模型目前上不太清楚,其实理解了感觉用处也不是特别大。

  • 相关阅读:
    POJ
    POJ
    HDU-3374 String Problem (最小最大表示法)
    HDU-2328 Corporate Identity (暴力)
    HDU-1238 Substrings (kmp)
    kmp处理题型总结
    Numpy用户指南
    Docker 容器连接
    Docker 镜像使用
    docker容器的使用
  • 原文地址:https://www.cnblogs.com/singmelody/p/2755223.html
Copyright © 2020-2023  润新知