維尼的蜂巢

RealTime??!! It’s amazing!!!!

自動化Singletion工具 五月 23, 2006

Filed under: VC++/C++/C — kevinlin @ 9:37 下午

看了一些程式碼,發現遊戲在做一些資源管理時都會用到singleton,雖說singleton這東西出來很久了,不過我也是這1-2個月才聽到,在以前呢!!我們是用全域變數來做singleton做的事的,現在有個比較好的singleton,那就來了解一下跟做一個自動化singleton工具吧!!!

什麼是Singleton呢?
Singleton物件在系統裡面不管什麼時候只有一個存在,也就是説從程式開始到結束都只能有一個這個物件,遊戲裡面常見的singleton像是貼圖、檔案或其他使用者介面等的管理機制,它們每個都是一個子系統,從遊戲開始到結束都存在,所以可這些系統可以用singleton來實作。

作遊戲時Singleton的一個常用的例子就是貼圖的管理(TextureMgr),我們可以讓它有GetTexture()和UseTexture()等方法。貼圖管理的目的包含有從檔案庫尋找貼圖、把它轉換成系統的圖形物件、讓它rasterizer、不用的時候釋放它。因為系統中只需要依個TextureMgr,所以理所當然的我們可以用singleton。

singleton的觀念跟全域變數一樣,我們可以把它當作全域變數,但是[為什麼不之階使用全域物件呢??]用全域物件的確很方便,但是呢因為全域物件的建構跟解構的順序會依實作跟平台而異,一般都無法預測。雖說有很多解決的方法,不過我們可以用singleton,它可以當作全域物件來使用,又可以控制建構和解構的順序。

[傳統做法]
singleton的標準作法:
TextureMgr& GetTextureMgr(void)
{
        static T s_Singleton;
        return (s_Singleton);
}
 因為在遊戲中某些子系統必須要比其他的子系統先結束,上面這是最簡單的使用方法,會在函數第一次被呼叫時建構,不過解構我們就不能控制了,所以我們要做一些改良的方法。

[改良方法1]
用一個指向singleton的指標來追蹤它的能力:
class TextureMgr
{
        static TextureMgr* ms_Singleton;
  public:
        TextureMgr(void) {ms_Singleton = this;  /*…*/ }
       ~TextureMgr(void) {ms_Singleton = 0; /*…*/ }
        // …
        TextureMgr& GetSingleton(void) {return (*ms_Singleton); }
};

現在這樣就可以隨心所欲的建立或銷毀TextureMgr了,而且直接呼叫GetSingleton就可以很簡單的存取singleton了。
不過這方法還是有一個小小的不方便的地方,就是每一個singleton類別都要加入相同的程式碼(追蹤singleton指標)。

[改良方法2]
這次用比較接近泛型概念的樣板來自動定義singleton指標,完成設定、查詢和清除,然後用assert()來確保不會產生超過一個的物件:
#include

template class Singleton
{
    static T* ms_Singleton;

public:
    Singleton( void )
    {
        assert( !ms_Singleton );
        int offset = (int)(T*)1 – (int)(Singleton *)(T*)1;
        ms_Singleton = (T*)((int)this + offset);
    }
   ~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 );  }
};

template T* Singleton ::ms_Singleton = 0;

我們要將任何的類別轉成singleton,只要三個簡單的步驟:
1.類別MyClass繼承Singleton
2.使用MyClass之前必須要先建構一個物件,建構的方法不是很重要,可以宣告成全域物件、區域靜態物件或者包裝在其他類別裡面,用new和delete自己來控制生成和銷毀的時機。不管用哪個方法,都可以用相同的介面來使用它。
3.使用那個物件前先呼叫MyClass::GetSingleton(),或是我們可以偷懶的用巨集把g_MyClass定義成MyClass::GetSingleton(),把它當作全域物件使用。
下面是一個範例:
class TextureMgr : public Singleton
{
public:
    Texture* GetTexture( const char* name );
    // …
};

#define g_TextureMgr TextureMgr::GetSingleton()

void SomeFunction( void )
{
    Texture* stone1 = TextureMgr::GetSingleton().GetTexture( “stone1″ );
    Texture* wood6 = g_TextureMgr.GetTexture( “wood6″ );
    // …
}
這個Singleton類別的唯一目的是當MyClass建構或解構時,為它們註冊或銷毀,從Singleton衍生MyClass單純是為了繼承這個方便的註冊機制。而且這個方法不會增加類別的大小,只有增加一些自動的函數呼叫。

它到底怎麼辦到的???重要的工作在Singleton建構子完成,他算出衍生物件的相對位址,把它儲存在singleton指標(ms_Singleton)。要注意的是衍生類別可能不只繼承自Singleton,此時MyClass的"this"可能有異於Singleton的"this",解決的方法是取一個不存在的物件,設定記憶體位置為0x1,cast成兩種類別,他差異值就是Singleton和衍生類別MyClass的距離,我們就用它設定singleton指標。

 

[references]
More Effective C++
Game Programming Gems 1

 

發表迴響

在下方填入你的資料或按右方圖示以社群網站登入:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / 變更 )

Twitter picture

You are commenting using your Twitter account. Log Out / 變更 )

Facebook照片

You are commenting using your Facebook account. Log Out / 變更 )

Google+ photo

You are commenting using your Google+ account. Log Out / 變更 )

連結到 %s