将void*转换为更高的东西的安全方法

Safe way of casting void* to something higher?

本文关键字:安全 方法 void 转换      更新时间:2023-10-16

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

我该怎么做?

没有办法检查您可以将void*转换为什么,除非您存储额外的信息,这些信息表明每个指针的实际类型。

一个更"c++的方式"去做你想做的是从一个抽象基类Resource派生每个资源类,并在你的资源管理器中存储一个指向Resource的指针映射。然后,您可以使用dynamic_cast<T*>转换为所需的类型,如果指针指向错误类型的对象,这将返回NULL。或者(取决于你想做什么)你可以简单地返回一个Resource*指针并使用虚函数来实现每个资源的功能。

您可以很容易地做到这一点,如果您扩展您保存的值类型-使其成为一个结构体,也保存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>来节省资源。

(我相信这已经回答了许多其他关于void指针的问题,但我们继续…)

但是因为这有点危险(因为那些void*指针可以任何东西),我想至少检查一下在运行时,如果转换正在进行这样我就能对它做出反应导致应用程序崩溃

不能检查。这就是void*指针的特点。你不知道它们指向的是什么,在不知道它们指向的内存类型的情况下,你不能(也不允许)检查它们指向的内存。

如果你有一个void*,你必须事先知道它真正指向什么,然后适当地强制转换。