从void Pointer(创建通用存储)铸造有什么问题

Is there something wrong in casting from void pointer (to create a generic storage)?

本文关键字:问题 什么 存储 Pointer void 创建      更新时间:2023-10-16

我正在尝试在C 中创建一个通用存储类。如果您查看Code Bellow,我想存储string/AnyType的地图并访问它们。

  class StoresTestC
  {
  public:
    template < class SettingsType >
    std::map< std::string, SettingsType >* getStore(std::string const&);
  private:
    std::map< std::string, void* > stores_;
  };
  template < class SettingsType >
  std::map< std::string, SettingsType >* StoresTestC::getStore(std::string const& name)
  {
    if (stores_.count(name) > 0)
    {
      void* temp = this->stores_[name];
      return (std::map< std::string, SettingsType >*)(temp);
    }
    else
    {
      std::map< std::string, SettingsType > * result = new std::map< std::string, SettingsType > ();
      this->stores_[name] = result;
      return result;
    }
  }

我看到了这两个明显的危险:

  1. 如果我以错误的SettingsType/name称呼它,我将据我所知(我可能是错)的错误铸件,将导致不确定的行为。

  2. 它将创建一个内存泄漏,但是我有一个解决方案(在这里也披露了两个很久了)。

还有其他可能出错的东西,您可以预见吗?

首先退后一步,并确保您真正想要做到这一点。然后再看一次设计。

好吧,您仍然需要此功能?

使用std::map<std::string, boost::any>,其中boost::any始终是map类型。然后,当您使用any_cast或任何机制将物品撤回时,您可以保证它是正确的类型或抛出的,因此您永远不会冒险不确定的行为。此外,由于any是按值,您也没有可能在周围漂浮的内存泄漏。

我还应该注意,在您的原始解决方案中,如果您使用shared_ptr<void*>,它将记住如何删除该shared_ptr中存储的原始类型,从而删除您提到的内存泄漏。

编辑:我看不到其他明显的技术问题。但是,请注意,具有这样的地图是可能的/可能导致认知(" Grokking")问题的问题,并且确实会增加代码的复杂性。

是的,您对点1的怀疑是对的(如您所提到的,应该可以解决第2点,但是请考虑所包含的地图的破坏者,应正确调用)。

我不会在不提供客户端加强 SettingsTypestd::string'typeName'表示中的简单机制的情况下将这种接口放入野外。一个简单的错别字会弄乱整个事情。我认为boost::any会在您尝试将值投入所需/预期结果类型的情况下表达这一点。

为什么在这里完全需要std::string表示?如果使用RTTI,则可以使用typeid()

隐藏用于stores_的密钥

,因为您必须将类型作为模板参数传递以获取地图,这是否只能每类映射?

namespace StoresTest {
  template<typename T>
  struct MapStruct {
    static std::map<std::string, T> the_map;
  };
  template<typename T> std::map<std::string, T> MapStruct<T>::the_map;
  template<typename T>
  inline std::map<std::string, T>& getStore()
  { return MapStruct<T>::the_map; }
}

然后,您可以通过执行

获得带有Value类型Foo的地图
std::map<std::string, Foo>& foo_map = StoresTest::getStore<Foo>();

您不再需要string来命名类型,但是如果您有其他理由需要它。