将void *铸造得更高的安全方法?

时间:2011-07-09 12:21:33

标签: c++ void-pointers

我有一个管理各种类型资源的泛型类,但由于我不想为每个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 *指针可以是任何东西),我想至少在运行时检查转换是否会起作用,所以我可以在没有应用程序崩溃的情况下对它做出反应。

我该怎么做?

3 个答案:

答案 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*,你必须事先知道它真正指向的是什么然后适当地施放。