原文:What happens to an STL iterator after erasing it in VS, UNIX/Linux?
翻译:
请考虑下面的脚本:
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 erase迭代器之后会等于end(),所以当尝试递增迭代器的时候会崩溃。真的会根据不同的编译期会产生不同的行为吗?如果是这样的话,在UNIX/Linux上,erase迭代器之后会发生什么呢?
Answers1:
是的,如果你 erase一个迭代器,它会得到一个叫做 singular的值,这意味着这个迭代器不再属于任何容器,你不能增加它、减少它或者读出/写入它。正确的循环代码应该是下面这个样子的:
for(map::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::vector 和std::deque起作用,但是不适合std::map 和std::set。
Answers2:
在 std::map的一个迭代器上调用 erase之后,它是无效的。这意味着你不能在使用它。尝试调用它(包括增加它)是无效的并且可能导致任何事情的发生(包括崩溃)。对于 std::map,调用 erase之后不会使任何其他迭代器失效,(只要迭代器不是 end())它就是有效的:
T2pS.erase( it++ );
当然,如果你正确的使用它,你不会想要在循环中无条件增加迭代器。
但是对于这个例子,为什么要在循环中删除它呢?为什么不在循环结尾调用 T2pS.clear()呢。
另一方面,你看起来想要 map的原生 second指针,但是 map似乎拥有指向的对象。在这种情况下,为什么不让 map的 second拥有智能指针呢,比如:std::tr1::shared_ptr。
【顺便说一句,我没有看到任何 map的模板参数。你是否在本地命名空间中使用 std::map作为 map的 typedef】
Answers3:
for (i = v.begin(); i != v.end(); ) {
//...
if (erase_required) {
i = v.erase(i);
} else {
++i;
}
}
Answers4:
我认为如果你修改集合,会使你的迭代器失效。 正如你发现的那样,你不能依赖这种行为。
网友评论