从地图中删除时出现奇怪的seg错误

时间:2013-04-23 20:48:10

标签: c++

我有以下代码:

//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),我仍然遇到了段错误。

6 个答案:

答案 0 :(得分:4)

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

您可以将循环更改为:

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);

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

答案 1 :(得分:4)

来自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;
    }
  }
}

答案 2 :(得分:2)

从地图中删除元素后,指向此元素将变为无效。因此spreadsheets.at(i).cells.erase(change.first);呈现it无效。见Iterator Invalidation Rules

答案 3 :(得分:0)

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

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

答案 4 :(得分:0)

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

SO

答案 5 :(得分:0)

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

//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;
        }
    }
}