8

请考虑以下场景:


map(T,S*) & GetMap(); //Forward decleration

map(T, S*) T2pS = GetMap();

for(map(T, S*)::iterator it = T2pS.begin(); it != T2pS.end(); ++it)
{
    if(it->second != NULL)
    {
        delete it->second;
        it->second = NULL;
    }
    T2pS.erase(it);
    //In VS2005, after the erase, we will crash on the ++it of the for loop.
    //In UNIX, Linux, this doesn't crash.
}//for

在我看来,在 VS2005 中,在“擦除”之后,迭代器将等于 end(),因此在尝试增加它时会崩溃。编译器之间在此处呈现的行为中真的存在差异吗?如果是这样,“擦除”之后的迭代器在 UNIX/Linux 中等于什么?

谢谢...

4

4 回答 4

22

是的,如果你删除一个迭代器,那个迭代器就会得到一个所谓的奇异值,这意味着它不再属于任何容器。您不能再增加、减少或读出/写入它。执行该循环的正确方法是:

for(map<T, S*>::iterator it = T2pS.begin(); it != T2pS.end(); T2pS.erase(it++)) {
    // wilhelmtell in the comments is right: no need to check for NULL. 
    // delete of a NULL pointer is a no-op.
    if(it->second != NULL) {
        delete it->second;
        it->second = NULL;
    }
}

对于在擦除一个迭代器时可能使其他迭代器失效的容器,erase返回下一个有效的迭代器。然后你用

it = T2pS.erase(it)

这就是它适用于std::vectorand的方式std::deque,但不适用于std::mapor std::set

于 2009-01-11T17:00:19.497 回答
5

在你调用erase一个迭代器到 astd::map之后,它就失效了。这意味着您不能使用它。尝试使用它(例如通过增加它)是无效的,并且可能导致任何事情发生(包括崩溃)。对于 a std::map,调用erase迭代器不会使任何其他迭代器无效,因此(例如)在此调用之后(只要itwas not T2pS.end()),它将是有效的:

T2pS.erase( it++ );

当然,如果您使用这种方法,您不会希望it在 for 循环中无条件地递增。

但是,对于这个例子,为什么要在 for 循环中进行擦除呢?为什么不在循环结束时调用 T2pS.clear() 。

另一方面,看起来您在地图的“右侧”有一个原始指针,但地图似乎拥有指向的对象。在这种情况下,为什么不把地图右边的东西做成某种智能指针,比如std::tr1::shared_ptr?

[顺便说一句,我没有看到任何模板参数到map. 您是否在本地命名空间中定义了std::mapas的特定实例化?]map

于 2009-01-11T16:58:04.727 回答
0

看到这个

for (i = v.begin(); i != v.end(); ) {
  //...
  if (erase_required) {
      i = v.erase(i);
  } else {
      ++i;
  }
}
于 2009-01-11T16:44:52.367 回答
0

我认为,如果您修改集合,您的迭代器就会失效。正如您所发现的,您不能依赖这种行为。

于 2009-01-11T16:46:39.913 回答