2011-10-12 58 views
1

我即將結束一個入門遊戲編程課程,希望將我在課堂上學到的技能與以前的OOP體驗相結合,創建一個用於製作2D遊戲的小型圖書館。然而,我目前的擔心是管理我的類的實例集合的最佳方式。對象管理 - 容器還是工廠?

我正在使用的庫(DarkGDK)完全由作用於整數的自由函數組成。當使用諸如dbSprite()之類的函數創建「對象」時,我們給它一個唯一的ID(int值)以引用它 - 一個「地址」排序。我個人發現這種方法很簡單,所以我創建了一些類來封裝每一組自由函數,如Image,Sprite和AnimatedSprite(這兩個Sprite類型在DarkGDK庫中是不同的)。

問題是,爲了使這些對象工作,我仍然需要傳遞一個唯一的ID給構造函數,以便根據適當的地址調用DarkGDK函數。我試圖擺脫由ID一起引用這些東西,但我在討論如何創建對象。目前,我有一些AssetManager類持有對創建的每個對象的引用,請檢查現有的ID並只允許唯一的ID,但這仍然無法解決被迫在管理類外部生成ID的問題。這讓我認爲工廠是最好的方法。

我知道在C#中我可以創建一個AssetFactory<T> where T:Asset,它可以輕鬆地爲每個資產調用適當的構造函數來創建實例,但據我所知,C++沒有這樣的設施。

所以我認爲我應該採取的方法是使用某種抽象的AssetFactory。 我的想法(正確與否)是AssetFactory的子項將跟蹤正在使用的ID並僅頒發適用對象唯一ID。事情是這樣的:

class Asset { 
    int m_index; 
    Asset(int index); 
}; 
class Image : public Asset { 
    Image(char* imgPath); 
    void Draw(); 
}; 
class Sprite : public Asset { 
    Sprite(Image* img); 
    void Draw(); 
}; 

class AssetFactory { 
private: 
    std::vector<Asset*> m_assets; 
    int GetUniqueID(); 
public: 
    AssetFactory(); 
    ~AssetFactory(); 

    virtual Asset* CreateAsset(); // but each class has different constructor parameters... 
}; 

class ImageFactory : public AssetFactory { 
    Asset* CreateAsset(char* imgPath); // ...so this wouldn't work (nor compile) 
}; 
class SpriteFactory : public AssetFactory { 
    Asset* CreateAsset(); // ...so will i be forced to call the default constructor and modify it later? 
}; 

這裏的問題是,正如上面提到的,不同的對象有不同的構造,使得這樣的設計毫無意義。我應該採取不同的方法嗎?或者我只是對工廠模式有錯誤的想法?

編輯:爲了澄清,我要爲精靈和圖片不同的工廠的原因是因爲它 admissable了雪碧和圖像具有相同的ID。 ID必須在同一「類型」的其他資產中唯一。

+0

我不知道爲什麼這是downvoted,但我投它回零。 –

+1

僅供參考:整數方法通常稱爲Handle,因爲您不是直接通過指針與數據結構進行交互,而是通過指針的句柄(整數)與數據結構進行交互。 – Daemin

+0

@Daemin:這是我一直在尋找的術語。謝謝。這是C中的常見做法嗎?儘管該庫是在MVC2008版本中發佈的,但它仍然是一個C++庫,儘管它的架構和行爲對我來說似乎非常類似C。 –

回答

0

其中C#有泛型,C++有模板。您不能輕易地在C++中爲泛型參數提供約束,但有多種方法可確保您只將派生自資產類型作爲模板參數。

爲了將適當的參數傳遞給構造函數,可以使用可變參數模板方法。目前我沒有一個編譯器可供使用......稍後我會用一個示例再次編輯,儘管您可以在stackoverflow上找到大量其他可變參數模板代碼。

+0

我知道可以使用的模板功能,但問題不在於確保創建的對象是資產類型,而是如何使用相關參數*調用相應的對象構造函數*。 –

+0

@wtfsven:編輯我的答案提及可變參數模板。 –

+0

C++ 03不支持可變模板,所以我在那裏運氣不佳。 –

0

也許我遺漏了一些東西,但我不清楚爲什麼無法在AssetManager中移動ID生成器,這樣可以隱藏來自外部世界的唯一ID的所有痛苦。

無論如何,如果您需要跟蹤ID,您將需要經理級別,據我現在可以告訴您的帖子。如果您使用工廠方法而不是工廠類,那麼您剛剛到達終點線:)唯一存在的問題是處理ID,但您可以在Asset類的虛擬析構函數中進行。如果你想保持它的乾淨,那麼你應該在管理器中爲它提供一個受保護的方法,並讓經理的Asset類析構函數(或某個Cleanup函數)成爲朋友。

+0

我實際上已經改變了我的架構來實現這種方法。現在我只是玩弄工廠模式。 :)感謝您的輸入。 –

1

如果你的庫允許任意的ID,並且你在一個相對平等的地址空間(例如sizeof(int)== sizeof(int *))中工作,這是一個相當微不足道的問題,我知道的所有32位編譯器。生成這些ID很簡單 - 只需reinterpret_cast指針即可。

class Sprite { 
    int GetUniqueID() { return reinterpret_cast<int>(this); } // easies 
public: 
    // public interface 
}; 

此外,它實際上可能不值得重新使用舊ID。只是繼續製造新的。我的意思是,你不會用一個32位整數的空間。

最後,你絕對不能在這裏使用運行時繼承。如果必須的話,使用編譯時混入。

+0

這是一個好主意!不幸的是,ID的最大值是USHRT_MAX,所以我需要做一些額外的操作來確保唯一性。 –

+0

謹慎使用reinterpret_cast。當你使用它時,你基本上是在告訴編譯器:「嘿,愚蠢,閉嘴,我完全知道我在做什麼。」 –

相關問題