【问题标题】:Weird seg fault when erasing from a map从地图中擦除时出现奇怪的段错误
【发布时间】:2013-04-17 05:33:08
【问题描述】:

我有以下代码:

//update it in the map
      std::map<std::string, std::string>::iterator it;
      for(it = spreadsheets.at(i).cells.begin(); it != spreadsheets.at(i).cells.end(); ++it)
      {
        if(it->first == change.first)
        {
          if(change.second == "")
          {
            spreadsheets.at(i).cells.erase(change.first);
          }
          else
          {
          it->second = change.second;
          }
        }
      }

上面的代码在我的 mac 上完美运行,但是当我在 linux 计算机上运行时,它会在 spreadsheets.at(i).cells.erase(change.first); 上引发 seg 错误

知道是什么导致了这个错误吗?我尝试将erase(change.first) 更改为erase(it),但仍然出现段错误。

【问题讨论】:

    标签: c++


    【解决方案1】:

    来自std::map::erase的文档:

    对已擦除元素的引用和迭代器无效。其他引用和迭代器不受影响。

    你的循环仍在继续,你增加你的(现在无效的)迭代器。

    修复:以另一种方式增加迭代器,例如:

    std::map<std::string, std::string>::iterator it;
    for (it = spreadsheets.at(i).cells.begin(); it != spreadsheets.at(i).cells.end();/*NOTE: no increment here*/)
    {
      if (it->first == change.first)
      {
        if (change.second == "")
        {
          it = spreadsheets.at(i).cells.erase(it); // C++11 only
          // in both C++03 and C++11:  spreadsheets.at(i).cells.erase(it++);
        }
        else
        {
          it->second = change.second;
          ++it;
        }
      }
      else
        ++it;
    }
    

    或者为了避免由于执行路径众多而造成的混淆(同样的混淆让我在第一次尝试时忘记了最后一个else):只需复制迭代器,增加原始迭代器,然后使用副本。在您的情况下,这可能看起来有点矫枉过正,但对于更复杂的循环,这有时是保持理智的唯一方法。 ;)

    std::map<std::string, std::string>::iterator it;
    for (it = spreadsheets.at(i).cells.begin(); it != spreadsheets.at(i).cells.end();/*NOTE: no increment here*/)
    {
      std::map<std::string, std::string>::iterator this_it = it++;
      if (this_it->first == change.first)
      {
        if (change.second == "")
        {
          spreadsheets.at(i).cells.erase(this_it);
        }
        else
        {
          this_it->second = change.second;
        }
      }
    }
    

    【讨论】:

    • @Deekor:增加一个无效的迭代器是未定义的行为,这意味着基本上“任何事情都可能发生”(它甚至可以具有工作的外观,这就是你的Mac)。
    • @Deekor:我更新了我的代码,有一条路径我忘记增加迭代器(最后一个else)。请务必仔细检查,否则您将遇到错误。
    • @syam,在 C++03 中,只需输入 spreadsheets.at(i).cells.erase(it++); 就足够了,因为后增量会返回迭代器的副本。
    • @W.B.好的,我会解决的。谢谢!
    • 感谢您的详细回答。因为我只需要更新一个项目,我就可以在“擦除”之后“中断”
    【解决方案2】:

    因为当您从容器中擦除时,您的迭代器不再有效,但您的循环仍在继续。

    您可以将循环更改为:

    std::map<std::string, std::string>::iterator it = spreadsheets.at(i).cells.begin();
    while (it != spreadsheets.at(i).cells.end())
    {
      if(it->first == change.first)
      {
        if(change.second == "")
        {
          spreadsheets.at(i).cells.erase(it++);  //Post increment returns a copy pointing at the current element, while it already points to the next element and thus stays valid after erase
        }
        else
        {
          it->second = change.second;
          ++it;
        }
      }
      else
        ++it;
    }
    

    现在我想起来了,为什么要用迭代器指向的对的第一个元素来擦除,即:

    spreadsheets.at(i).cells.erase(change.first);
    

    而不是

    spreadsheets.at(i).cells.erase(it);
    

    效率较低,因为必须进行另一次查找。

    【讨论】:

      【解决方案3】:

      从映射中删除指向该元素的元素后将失效。因此spreadsheets.at(i).cells.erase(change.first); 呈现it 无效。见Iterator Invalidation Rules

      【讨论】:

        【解决方案4】:

        对已擦除元素的引用和迭代器无效。

        //update it in the map
        std::map<std::string, std::string>::iterator it;
        for(it = spreadsheets.at(i).cells.begin(); it != spreadsheets.at(i).cells.end(); ++it)
        {
            if(it->first == change.first)
            {
                if(change.second == "")
                {
                    spreadsheets.at(i).cells.erase(it--);
                }
                else
                {
                    it->second = change.second;
                }
            }
        }
        

        【讨论】:

          【解决方案5】:

          在你做spreadsheets.at(i).cells.erase(change.first); 的那一刻,std::map 中的迭代器(在当前的 change.first 键处)是无效的。因此,当您执行 it++ 时,这是未定义的行为。

          cf Rules for Iterator Invalidation 了解有关标准容器中迭代器无效的规则

          【讨论】:

            【解决方案6】:

            在删除之前增加迭代器。为什么它没有发生在 Mac 上?谁知道......不同的操作系统,不同的行为。

            SO

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2020-03-12
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2017-05-22
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多