【问题标题】:Is it wise to use a pointer to access values in an std::map使用指针访问 std::map 中的值是否明智?
【发布时间】:2013-05-08 14:03:46
【问题描述】:

std::map::find 返回指向数据的指针并使用它而不是获取数据的副本是否危险?

目前,我获得了指向地图中条目的指针,并将其传递给另一个函数以显示数据。我担心项目移动导致指针无效。这是一个合法的问题吗?

这是我的示例函数:

MyStruct* StructManagementClass::GetStructPtr(int structId)

{
    std::map<int, MyStruct>::iterator foundStruct;
    foundStruct= myStructList.find(structId);
    if (foundStruct== myStructList.end())
    {
        MyStruct newStruct;
        memset(&newStruct, 0, sizeof(MyStruct));
        myStructList.structId= structId;
        myStructList.insert(pair<int, MyStruct>(structId, newStruct));

       foundStruct= myStructList.find(structId);
   }

   return (MyStruct*) &foundStruct->second;

}

【问题讨论】:

  • 我认为您的意思是迭代器而不是指针?
  • 你能显示一些代码吗? “从 map.find 返回指针”是指迭代器吗?在调用 .find() 的函数退出之前,迭代器将一直有效。

标签: c++ pointers stdmap


【解决方案1】:

毫无疑问,返回迭代器比返回指针更典型,尽管它可能没什么区别。

就保持有效而言:地图迭代器保持有效,直到/除非它所引用的项目被从地图中删除/删除。

当您在地图中插入或删除某些 other 节点时,可能会导致地图中的节点被重新排列。这是通过操作节点之间的指针来完成的,因此它会更改其他节点包含指向您关心的节点的指针的内容,但不会更改该特定节点的地址或内容,因此指向该节点的指针/迭代器仍然有效。

【讨论】:

  • 向地图添加/删除其他元素是否会影响该迭代器的算法?
  • @nims: 否 -- 除非它所引用的项目已从地图中删除,否则迭代器仍然有效,因此您可以像使用任何其他迭代器一样使用它(好吧,任何其他迭代器进入地图,无论如何)。
  • 在我的示例中,我返回了一个指向迭代器->second 的指针,而不是实际的 map[key]->second。是这样吗?
  • @Jason:是的,看起来差不多。但是,我应该补充一点,代码对我来说看起来不太好——在使用 memset 的不必要的演员表和大致相当于 return your_map.insert(...).second; 的整个事情之间,它只是不会跳该页面是非常干净的代码。
  • 我不希望它是最干净的代码。我是 stl 的新手,所以我不断地对其进行修改以了解更多信息。我在下面提到演员一开始不在那里,但我试图找出一个错误。 memset有什么问题?我有不会全部填充的字段,我知道它们没有被初始化为 0,如果不使用,这是我需要它们的值。
【解决方案2】:

只要您、您的代码和您的开发团队了解 std::map 值的生命周期(insert 之后有效,eraseclearassignoperator= 之后无效),然后使用 iteratorconst_iterator::mapped_type*::mapped_type const* 都是有效的。另外,如果返回总是保证存在,那么::mapped_type&amp;::mapped_type const&amp;也是有效的。

就明智而言,我更喜欢 const 版本而不是可变版本,我更喜欢引用而不是指针而不是迭代器。

返回一个迭代器和一个指针是不好的:

  • 它公开了一个实现细节。
  • 使用起来很尴尬,因为调用者必须知道取消对迭代器的引用,结果是 std::pair,然后必须调用 .second 才能获得实际值。
    • .first 是用户可能不关心的密钥。
  • 确定迭代器是否无效需要知道::end(),而这显然对调用者不可用。

【讨论】:

    【解决方案3】:

    这并不危险 - 只要迭代器或引用有效,指针就会保持有效。

    但是,在您的特定情况下,我认为这无论如何都不是正确的事情。您的函数无条件地返回结果。它从不返回 null。那么为什么不返回参考呢?

    此外,您的代码中还有一些 cmets。

    std::map<int, MyStruct>::iterator foundStruct;
    foundStruct = myStructList.find(structId);
    

    为什么不将声明和赋值合并到初始化中呢?然后,如果你有 C++11 支持,你可以写

    auto foundStruct = myStructList.find(structId);
    

    然后:

      myStructList.insert(pair<int, MyStruct>(structId, newStruct));
      foundStruct = myStructList.find(structId);
    

    您可以使用 make_pair 简化插入。您还可以避免冗余查找,因为 insert 返回一个指向新插入元素的迭代器(作为一对中的第一个元素)。

      foundStruct = myStructList.insert(make_pair(structId, newStruct)).first;
    

    最后:

    return (MyStruct*) &foundStruct->second;
    

    永远不要使用 C 风格的强制转换。它可能不会达到您的预期。另外,不要在不需要时使用强制转换。 &foundStruct->second 已经具有 MyStruct* 类型,那么为什么要插入强制转换呢?它唯一能做的就是隐藏一个您需要更改的地方,例如,如果您更改地图的值类型。

    【讨论】:

    • 演员表是我正在调试的事情。它原本不在那里。我会拿出来的。
    【解决方案4】:

    是的,

    如果你构建一个泛型函数而不知道它的用途,返回指针(或迭代器)可能会很危险,因为它可能变得无效。

    我建议做以下两个之一:
    1. 使用 std::shared_ptr 并返回它。 (见下文)
    2. 按值返回结构体(可以慢一些)

    //change the difination of the list to
    std::map<int, std::shared_ptr<MyStruct>>myStructList;
    
    std::shared_ptr<MyStruct> StructManagementClass::GetStructPtr(int structId)
    {
        std::map<int, std::shared_ptr<MyStruct>>::iterator foundStruct;
        foundStruct = myStructList.find(structId);
        if (foundStruct == myStructList.end())
        {
            MyStruct newStruct;
            memset(&newStruct, 0, sizeof(MyStruct));
            myStructList.structId= structId;
            myStructList.insert(pair<int, shared_ptr<MyStruct>>(structId, shared_ptr<MyStruct>(newStruct)));
    
           foundStruct= myStructList.find(structId);
       }
    
       return foundStruct->second;
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-05-18
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多