2013-09-10 196 views
0

我有一個UIManager來管理從一個UI類繼承的一系列類。目前,它的工作原理是這樣的,其中單個用戶界面懶洋洋地初始化,靜態存儲:模板和延遲初始化

class UIManager 
{ 

public: 
    UIManager();   // Constructor 
    virtual ~UIManager(); // Destructor 


    template <typename T> 
    T *getUI() 
    { 
     static T ui();  // Constructs T, stores result in ui when 
          // getUI<T>() is first called 
     return &ui;  
    } 
} 

一個名爲:

getUI<NameEntryUI>()->activate(); 

getUI<MenuUI>()->render(); 

我正在考慮設計改變這將允許我有多個玩家,因此不止一個遊戲窗口,因此不止一個UIManager。當UIManager被刪除時,我希望所有構建的ui對象都被清除(目前,因爲ui對象是靜態的,它們會一直存在,直到程序退出)。

如何重寫上面的內容以在UIManager被終止時刪除ui對象?

======================================

下面是解我已經實施。早期的結果是它運行良好。

基本上,我從Potatoswatter建議的想法開始,我喜歡這個想法,因爲它與我之前開始的方法相似,因爲我不知道typeid(T)。我向後移植了代碼以僅使用C++ 98功能。整個事情的關鍵是typeid(T),它允許您以一致的方式將實例化的接口映射到它們的類型。

class UIManager 
{ 
    typedef map<const char *, UserInterface *> UiMapType; 
    typedef UiMapType::iterator UiIterator; 

    map<const char *, UserInterface *> mUis; 

public: 
    UIManager();   // Constructor 
    virtual ~UIManager() // Destructor 
    { 
     // Clear out mUis 
     for(UiIterator it = mUis.begin(); it != mUis.end(); it++) 
     delete it->second; 

     mUis.clear(); 
    } 

    template <typename T> 
    T *getUI() 
    { 
     static const char *type = typeid(T).name(); 

     T *ui = static_cast<T *>(mUis[type]); 
     if(!ui) 
     ui = new T(); 

     mUis[type] = ui; 

     return ui; 
    } 
} 

回答

2

當前,您只有爲每種類型的一個UI元素分配了存儲空間。保持這個原則還有許多窗口是根本不可能的。

快速和骯髒的解決方案將是爲窗口號添加一個模板參數。如果它是一款遊戲,而且玩家數量有限,那麼您可以在預定數量的窗口中使用靜態存儲。

template <typename T, int N> 
T *getUI() 

綁UI身份類型系統的做法基本上是錯誤的,不過,我會使用多態和容器建議更傳統的方法。識別按類型的對象,但他們動態存儲,


一種方法可能看起來像

class UIManager { 
    std::map< std::type_index, std::unique_ptr<UIBase> > elements; 

    template< typename T > 
    T & GetUI() { // Return reference because null is not an option. 
     auto & p = elements[ typeid(T) ]; 
     if (! p) p.reset(new T); 
     return dynamic_cast< T & >(* p); 
    } 
} 

注意,這需要UIBase有一個虛析構函數,或對象將不會被終止正確的時候你退出。

+0

存儲'std :: shared_ptr '將意味着類型不需要公共基類或虛擬析構函數 –

+1

@JonathanWakely雖然沒有共享,但會有點濫用。嚴重的是,沒有多態的UI是瘋狂的。如果沒有虛擬方法來繪製和響應點擊,至少,程序組織有一些非常錯誤的地方。 – Potatoswatter

+0

正如我在我的問題中所述,我所有的UI對象都是從一個共同的父對象繼承而來的(在這裏沒有任何問題:-)。我試圖創建一個這樣的地圖,但沒想到使用unique_ptr。唯一的缺點是unique_ptr是一個C++ 11項目,我們還沒有完全接受。 – Watusimoto

1

由於您明確需要每種類型的多個對象,因此我們只需將對象存儲在std::map<UIManager const*, T>中。要拉出特定的管理對象,可以在地圖上查找相應的類型。棘手的位以後擺脫其使用功能對象的列表處理的對象:

class UIManager 
{ 
    std::vector<std::function<void()>> d_cleaners; 
    UIManager(UIManager const&) = delete; 
    void operator=(UIManager const&) = delete; 
public: 
    UIManager(); 
    ~UIManager(); 

    template <typename T> 
    T *getUI() { 
     static std::map<UIManager const*, T> uis; 
     typename std::map<UIManager const*, T>::iterator it = uis.find(this); 
     if (it == uis.end()) { 
      it = uis.insert(std::make_pair(this, T())).first; 
      this->d_cleaner.push_back([it, &uis](){ uis.erase(it); }); 
     } 
     return &(it->second); 
    } 
}; 

各個getUI()函數存儲地圖映射UIManager的地址,即,this,向相應的對象。如果沒有這樣的映射,則插入新的映射。另外,爲了確保清理對象,在this中註冊了清理函數,只需在剛剛從相應的地圖中獲取的迭代器中使用erase()即可。代碼沒有經過測試,但沿着這些線應該工作。

+0

如果UI對象是多態的,就像通常的做法一樣,應該有一個虛擬函數來執行視覺清理。 (這將由析構函數完成,但它們應該是異常安全的。) – Potatoswatter

+0

@Patatoswatter:我不明白你的意思!是的,事情可以做不同的事情,但這似乎是重點。使用'getUI ()'獲得正確類型肯定有一些優勢:您可以直接調用成員函數。 –

+0

避免虛擬調度的好處是性能,但在這種情況下根本不算。 – Potatoswatter