【问题标题】:Can a C++ map contain a pointer to my arbitrary class?C++ 映射可以包含指向我的任意类的指针吗?
【发布时间】:2012-07-09 20:51:43
【问题描述】:

我正在编写一个小型游戏引擎作为一个夏季项目,并且在 STL 地图方面有点挣扎。

我已经声明了一个类RenderList 来保存对象。 RenderList 将被传递给 Renderer 类来完成这项工作。

RenderList 有一个map<std::string,Entity*> objects;

一切正常,直到我尝试从地图中获取 Entity* 并得到:

断言失败,在 vc/include/xtree 表达式:map/set 迭代器不可取消引用。

这是检索指针的代码。

Entity* RenderList::getByName(std::string str){
    return objects.find(str)->second;
}

我需要它来保存一个指针而不是实际的对象,因为我需要 Entity 的不同子类。

我对 STL 还很陌生,我不应该在地图中存储指针吗?

当然应该允许我这样做,还是存储对象更好?

最后,我是不是做错了!?

希望这个问题不是重复的,我事先做了快速搜索。另外,如果这在 GameDev Stack 中会更好,我会在那里发布。

【问题讨论】:

    标签: c++ pointers stl map


    【解决方案1】:

    如果未找到该键,则 map::find(key) 返回一个“过去的”迭代器,即 map::end() 返回的值,并且该迭代器不指向任何元素,因此无法取消引用。在取消引用之前,您不会检查 find 返回的内容。

    你确定他们的钥匙在地图上吗?

    如果找不到键,您可能想要执行类似 return NULL 的操作,您可以通过将返回的迭代器与 end 进行比较来检查,例如

    Entity* RenderList::getByName(std::string str){
      map_type::iterator it = objects.find(str);
      if (it == objects.end())
        return NULL;
      return it->second;
    }
    

    RenderList 定义 typedef:

    typedef map<std::string,Entity*> map_type;
    

    (注:我总是让我的类为我用作实现细节的容器定义 typedef,因为编写 map_type::iterator 比编写 map&lt;std::string,Entity*&gt;::iterator 容易得多,而且如果我将容器更改为某些东西否则我不必将使用它的所有代码更改为例如map&lt;std::string,shared_ptr&lt;Entity&gt;&gt;::iterator,我可以将其保留为map_type::iterator,它仍然可以正常工作。)

    关于一般设计,您可以存储boost::shared_ptr&lt;Entity&gt;std::tr1::shared_ptr&lt;Entity&gt; 而不是原始指针吗?管理对象生命周期会更加安全和简单。

    【讨论】:

    • 非常感谢。这确实是问题所在。我没有注意到这个实体是在更高的地方创建的。我从未使用过 boost,坦率地说,它让我有点害怕,我不想深入研究更多我还不太了解的库。我想我会在某个时候,然后我会带着更多问题回来!
    • @Finlaybob,我认为这是一个很大的错误,非常严重。没有理由一次使用(甚至查看)所有 Boost。使用boost::shared_ptr 就像包含一个标头一样简单,并且可以防止您使用指向动态分配对象的原始指针可能会犯的很多 错误。 C++ 在某些人中因复杂的内存处理而名声不佳的原因仅仅是因为有些人拒绝使用像shared_ptr 这样的工具,这些工具使它变得简单和安全。即使你从不接触 Boost 的其余部分,你也应该尽快停止管理内存。
    【解决方案2】:

    这可能意味着您要查找的名称在地图中不存在。如果key不存在,find方法返回map的end迭代器,确实不可解引用。

    如果“未找到”的情况是自然发生的,那么您可以这样做

    Entity* RenderList::getByName(std::string str){
        map<std::string,Entity*>::iterator it = objects.find(str);
        return it != objects.end() ? it->second : NULL;
    }
    

    从而将处理这种情况的责任转嫁给调用者。

    如果“未找到”的情况不应该发生,要么抛出异常,要么至少这样做

    assert(it != objects.end());
    

    【讨论】:

      【解决方案3】:

      这样做的一个问题是您需要处理没有匹配str 的条目的情况。不确定具体的错误是什么,因为我(遗憾的是)在地图中进行了同样的操作以检索指针..

      【讨论】:

        【解决方案4】:

        如果映射不包含您传递给find 方法的键,那么它将返回objects.end()。取消引用这是一个运行时错误,可能会导致您看到的错误。试试吧:

        map<std::string,Entity*>::iterator findIt;
        findIt = objects.find(str);
        
        if ( findIt != objects.end() )
        {
            return findIt->second;
        }
        else
        {
           // Handle error here
        }
        

        【讨论】:

          【解决方案5】:

          正如其他人所指出的,您正在查找的名称在地图中不存在。大家很快建议你检查.find()的返回值。相反,我建议你不要打电话给.find()。以下是我将如何解决您的问题:

          Entity* RenderList::getByName(std::string str){
            return objects[str];
          }
          

          在上面的代码中,查找不存在的映射条目将创建一个指针值为 NULL 的条目,并返回 NULL。

          您需要在某处添加一些代码来检查是否存在空指针,然后再使用它。

          【讨论】:

          • 好吧,问题是为不存在的值创建一个新条目是否是个好主意。此外,名为getByName 的方法最终可能会被声明为const,这将使运算符[] 不可用。
          • 一般来说这不是你想要做的。如果您为每个有效键查找大量无效键,您将用无效条目填充映射并显着减慢查找速度。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2023-03-17
          • 1970-01-01
          • 2020-06-14
          • 1970-01-01
          • 2010-10-05
          • 1970-01-01
          • 2017-08-08
          相关资源
          最近更新 更多