我有一个UIManager,它管理一系列从单个UI类继承的类。目前,它的工作原理是这样的,其中各个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;
}
}
答案 0 :(得分: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
拥有虚拟析构函数,否则退出时对象将无法正常终止。
答案 1 :(得分: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()
。代码未经测试,但这些代码应该有效。