2013-09-10 155 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