1

我有以下代码:

//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 计算机上运行时,它会抛出一个 seg 错误spreadsheets.at(i).cells.erase(change.first);

知道是什么导致了这个错误吗?我试过改成erase(change.first)erase(it)但我仍然遇到段错误。

4

6 回答 6

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;
    }
  }
}
于 2013-04-23T20:54:52.933 回答
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);

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

于 2013-04-23T20:51:57.513 回答
2

从映射中删除指向该元素的元素后,该元素将失效。从而spreadsheets.at(i).cells.erase(change.first);使it无效。请参阅迭代器无效规则

于 2013-04-23T20:53:20.247 回答
1

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

//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;
        }
    }
}
于 2015-09-02T09:47:38.163 回答
0

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

cf迭代器失效规则,了解有关标准容器中迭代器失效的规则

于 2013-04-23T20:52:24.827 回答
0

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

所以

于 2013-04-23T20:56:40.653 回答