擦除'擦除后映射迭代器行为

时间:2015-02-26 07:07:11

标签: c++ dictionary stl

我在下面写这段代码,发现了这种奇怪的行为:

#include <iostream>
#include <map>
#include <string>
using namespace std;

int main()
{
   map<int, string> map1;
   map1[1] = "Hello";
   map1[2] = "Hello1";
   map1[3] = "Hello2";
   map1[4] = "Hello3";
   map1[5] = "Hello4";

   map<int, string>::iterator it;

   for (it = map1.begin(); it != map1.end(); /*it++*/)
   {
      cout << "Enter: " << (int)(it->first) << endl;
      if (it->first == 3)
         map1.erase(it);
      it++;
      cout << "Exit: " << (int)(it->first) << endl;

   }

   return 0;
}

输出结果为:

Enter: 1
Exit: 2
Enter: 2
Exit: 3
Enter: 3
Exit: 4
Enter: 4
Exit: 5
Enter: 5
Exit: 4

当我仅在for循环中增加迭代器it时(检查注释的迭代器),输出如下:

Enter: 1
Exit: 1
Enter: 2
Exit: 2
Enter: 3
Exit: 3
Enter: 4
Exit: 4
Enter: 5
Exit: 5

我很困惑为什么在第一种情况下,当我增加迭代器时,它再次指向前一个地图元素4?

2 个答案:

答案 0 :(得分:2)

两个版本都是未定义的行为,后者似乎有效。

在这两种情况下,都会增加已失效的迭代器,
例如,参见iterator invalidation rules
后者的行为有所不同,因为&#39;进入&#39;和&#39;退出&#39;正在打印相同的迭代器

这可以通过&#34; iterate-erase&#34;来解决。成语,所有STL容器都支持

// C++11
auto iter = container.begin();
while( iter != container.end() )
{
    if( SomeCondition() )
        iter = container.erase(iter);
    else
        ++iter;
}

这就是擦除返回迭代器

的原因

请注意关联容器,例如std::map
在C ++ 11之前不支持这个

答案 1 :(得分:1)

将迭代器解除引用到map1.end()会产生一个未定义的结果,这正是你在最后一次循环迭代中在cout中运行it->first时所做的。

在第一个版本中,您运行++it,到达map1.end()。从概念上讲,map1.end()不指向任何元素,解除引用它可以提供任何随机结果(包括SEGFAULT)。在您的情况下,它可能指向包含4的陈旧内存。

在第二个变体中,您永远不会取消引用map1.end(),因为一旦到达容器的末尾,循环条件就会退出。你总是在循环中打印出相同的元素。

还要注意上面的答案 - 擦除元素会使迭代器无效,因此两个变体仍然无效。