我有一个管理各种类型资源的泛型类,但由于我不想为每个T创建一个ResourceManager实例(因此每个类型T都有一个资源管理器),我必须使ResourceManager类的T类型未知。
我这样做是通过保存void *指针的映射并将其转换回所需的格式,如果有人从模板化的Load()方法中请求某种类型的话;
template <typename T>
T* Load(const std::string &location)
{
//do some stuff here
//everybody take cover!!!
return static_cast<T*>(m_resources[location]);
}
我使用模板特化来向班级引入不同的加载器:
template<>
AwesomeType* Load(const std::string &location)
{
//etc.
return static_cast<AwesomeType*>(m_resources[location]);
}
我知道这很难看,但现在没办法解决。我可以在专门的Load方法内部引入静态映射,但是这样我就无法将资源的生命周期绑定到ResourceManager对象的生命周期,这是一个必不可少的特性。
但是因为这有点危险(因为那些void *指针可以是任何东西),我想至少在运行时检查转换是否会起作用,所以我可以在没有应用程序崩溃的情况下对它做出反应。
我该怎么做?
答案 0 :(得分:7)
除非您存储指示每个指针的实际类型的其他信息,否则无法检查您可以将void*
转换为什么。
执行所需操作的更多“C ++方法”是从抽象基类Resource
派生每个资源类,并在资源管理器中存储指向Resource
的指针映射。然后,您可以使用dynamic_cast<T*>
转换为所需类型,如果指针指向错误类型的对象,则返回NULL
。或者(取决于你想做什么)你可以简单地返回一个Resource*
指针并使用虚函数来实现每个资源的功能。
答案 1 :(得分:6)
如果扩展保存的值类型,可以轻松地执行此操作 - 使其成为同时保存type_info
对象的结构:
#include <type_info>
struct ResourceInfo
{
std::type_info const& info;
void* ptr;
};
// ...
// just to give you the general idea
template<class Res>
void CacheResource(std::string const& location, Res* res)
{
ResourceInfo ri = { typeid(Res), res };
m_resources.insert(std::make_pair(location, ri));
}
template<class Res>
Res* Load(std::string const& location)
{
map_type::const_iterator res_it = m_resources.find(location);
if(res_it != m_resources.end())
{
if(typeid(Res) != res_it->second.info)
{
throw SorryBuddyWrongResourceType(some_info_here);
}
return static_cast<Res*>(res_it->second.ptr);
}
}
这与我的操作类似,但我使用shared_ptr<void>
来保存资源。
答案 2 :(得分:5)
(我确信已经有很多关于void指针的问题已经回答了,但是我们在这里......)
但是因为这有点危险 (因为那些void *指针可以 什么),我想至少检查一下 在运行时如果转换正在进行 工作,所以我可以没有做出反应 让应用程序崩溃。
你无法检查。这是关于void *指针的事情。你不知道他们指向的是什么,你不能(不允许)在不知道其类型的情况下检查他们指向的记忆。
如果你有void*
,你必须事先知道它真正指向的是什么然后适当地施放。