map和vector的迭代器失效问题

当删除一个STL容器(比如map, vector)中的某个元素时, 会引起迭代器失效, 所以, 我们务必提高警惕。

题目: 删除map<int, int>中value为5的倍数的元素。 该题看起来很自然很简单, 实则有迭代器失效的陷阱。

如果对迭代器失效问题一无所知, 则很容易写出如下的错误代码:

 1 #include <iostream>
 2 #include <map>
 3 using namespace std;
 4
 5 typedef map<int, int> Map;
 6 typedef map<int, int>::iterator MapIt;
 7
 8 void print(Map &m)
 9 {
10     MapIt it;
11     for(it = m.begin(); it != m.end(); it++)
12     {
13         cout << it->second << " ";
14     }
15
16     cout << endl;
17 }
18
19 void deleteValueFromMap(Map &m, int n = 5)
20 {
21     MapIt it;
22     for(it = m.begin(); it != m.end(); it++)
23     {
24         if(0 == it->second % n)
25         {
26             m.erase(it);
27         }
28     }
29 }
30
31 int main()
32 {
33     Map m;
34     int i = 0;
35     for(i = 0; i < 21; i++)
36     {
37         m[i] = i;
38     }
39
40     print(m);
41
42     deleteValueFromMap(m); // 程序崩溃
43
44     return 0;
45 }

运行上述程序, 结果程序崩溃,什么原因呢? 原来, 对于关联的容器map来说, m.erase(it);后, it就失效了, 而for循环中有it++, 自然而然会出问题啊。 那怎么办呢? 且看:

  1 #include <iostream>
  2 #include <map>
  3 using namespace std;
  4
  5 typedef map<int, int> Map;
  6 typedef map<int, int>::iterator MapIt;
  7
  8 void print(Map &m)
  9 {
 10     MapIt it;
 11     for(it = m.begin(); it != m.end(); it++)
 12     {
 13         cout << it->second << " ";
 14     }
 15
 16     cout << endl;
 17 }
 18
 19 void deleteValueFromMap(Map &m, int n = 5)
 20 {
 21     MapIt it;
 22     MapIt tmp;
 23     for(it = m.begin(); it != m.end(); /*不能再自增了*/)
 24     {
 25         if(0 == it->second % n)
 26         {
 27             tmp = it++;
 28             m.erase(tmp);
 29         }
 30         else
 31         {
 32             it++;
 33         }
 34     }
 35 }
 36
 37 int main()
 38 {
 39     Map m;
 40     int i = 0;
 41     for(i = 0; i < 21; i++)
 42     {
 43         m[i] = i;
 44     }
 45
 46     print(m);
 47
 48     deleteValueFromMap(m); // 程序ok
 49     print(m);
 50
 51     return 0;
 52 }
 53        结果为:
 54 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
 55 1 2 3 4 6 7 8 9 11 12 13 14 16 17 18 19
 56
 57
 58
 59
 60
 61        当然, 上述程序也可以继续简化为:
 62
 63
 64
 65 #include <iostream>
 66 #include <map>
 67 using namespace std;
 68
 69 typedef map<int, int> Map;
 70 typedef map<int, int>::iterator MapIt;
 71
 72 void print(Map &m)
 73 {
 74     MapIt it;
 75     for(it = m.begin(); it != m.end(); it++)
 76     {
 77         cout << it->second << " ";
 78     }
 79
 80     cout << endl;
 81 }
 82
 83 void deleteValueFromMap(Map &m, int n = 5)
 84 {
 85     MapIt it;
 86     for(it = m.begin(); it != m.end(); /*不能再自增了*/)
 87     {
 88         if(0 == it->second % n)
 89         {
 90             m.erase(it++);
 91         }
 92         else
 93         {
 94             it++;
 95         }
 96     }
 97 }
 98
 99 int main()
100 {
101     Map m;
102     int i = 0;
103     for(i = 0; i < 21; i++)
104     {
105         m[i] = i;
106     }
107
108     print(m);
109
110     deleteValueFromMap(m); // 程序ok
111     print(m);
112
113     return 0;
114 }
115        结果ok.

有的朋友肯定会问, m.erase(it++);就不会产生迭代器失效么? 确实不会! 为什么呢? 这样从it++说起, 为了简便起见, 我们用p++来代替吧。 看程序:

 1 #include <iostream>
 2 using namespace std;
 3
 4 int main()
 5 {
 6     char szTest[] = "abcdefg";
 7     char *p = szTest;
 8
 9     cout << *p++ << endl;
10
11     return 0;
12 }

大家都知道, 结果为a.  但是, 很多人错误地以为是先执行*p, 然后执行p++, 其实, 这是个天大的误解。 大家可以查一下*和++的执行顺序, 虽然*和++的优先级相同, 但此处采取的是右结合方式, 实际上先执行的是p++, 不过, p++的返回值是原来的p, 也就是说, *p++的值还是a.

所以, 在m.erase(it++);中,it++先执行, 此时还没有erase, 程序自然不会崩溃. 当it++执行完后, 已经指向了下一个元素了, 但it++的返回值还是当前元素, 此时再删除它, 合情合理。

OK,  map的迭代器失效问题,先介绍到这里。

那vector又是怎样的现象呢? 看程序:

  1 #include <iostream>
  2 #include <vector>
  3 using namespace std;
  4
  5 typedef vector<int> Vec;
  6 typedef vector<int>::iterator VecIt;
  7
  8 void print(Vec &v)
  9 {
 10     VecIt it;
 11     for(it = v.begin(); it != v.end(); it++)
 12     {
 13         cout << *it << " ";
 14     }
 15
 16     cout << endl;
 17 }
 18
 19 void deleteValueFromVector(Vec &v, int n = 5)
 20 {
 21     VecIt it;
 22     for(it = v.begin(); it != v.end(); it++)
 23     {
 24         if(0 == *it % n)
 25         {
 26             v.erase(it);
 27         }
 28     }
 29 }
 30
 31 int main()
 32 {
 33     Vec v;
 34     int i = 0;
 35     for(i = 0; i < 21; i++)
 36     {
 37         v.push_back(i); // 不能再傻傻地用下标了
 38     }
 39
 40     print(v);
 41
 42     deleteValueFromVector(v); // 程序崩溃
 43     print(v);
 44
 45     return 0;
 46 }
 47        运行程序, 结果程序也崩溃, 可见, vector也存在迭代器失效的问题, 怎么改呢? 如下:
 48
 49
 50
 51
 52
 53 #include <iostream>
 54 #include <vector>
 55 using namespace std;
 56
 57 typedef vector<int> Vec;
 58 typedef vector<int>::iterator VecIt;
 59
 60 void print(Vec &v)
 61 {
 62     VecIt it;
 63     for(it = v.begin(); it != v.end(); it++)
 64     {
 65         cout << *it << " ";
 66     }
 67
 68     cout << endl;
 69 }
 70
 71 void deleteValueFromVector(Vec &v, int n = 5)
 72 {
 73     VecIt it;
 74     for(it = v.begin(); it != v.end(); /*不能再自增了*/)
 75     {
 76         if(0 == *it % n)
 77         {
 78             v.erase(it); // vector元素自动向前挪动了(关联的map容器元素不会自动挪动), 所以不能再把it进行++了
 79         }
 80         else
 81         {
 82             it++;
 83         }
 84     }
 85 }
 86
 87 int main()
 88 {
 89     Vec v;
 90     int i = 0;
 91     for(i = 0; i < 21; i++)
 92     {
 93         v.push_back(i); // 不能再傻傻地用下标了
 94     }
 95
 96     print(v);
 97
 98     deleteValueFromVector(v); // 程序ok
 99     print(v);
100
101     return 0;
102 }
103         结果为:
104
105
106
107 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
108 1 2 3 4 6 7 8 9 11 12 13 14 16 17 18 19
109
110         可见, vector迭代器失效的问题也不容忽视。

总之, 下次说到迭代器失效(尤其涉及到容器大小的改变, 比如删除, 插入)时, 一定要留个心眼。  在实际编程中, 如果稍不注意, 则可能引起非常难以捕捉的bug, 折磨你我, 何不提前防范呢?

时间: 2024-10-12 16:51:48

map和vector的迭代器失效问题的相关文章

STL容器迭代器失效问题讨论

有的朋友肯定会问, m.erase(it++);就不会产生迭代器失效么? 确实不会! 为什么呢? 这样从it++说起, 为了简便起见, 我们用p++来代替吧. 看程序: #include <iostream> using namespace std; int main() { char szTest[] = "abcdefg"; char *p = szTest; cout << *p++ << endl; return 0; } 大家都知道, 结果

转:STL迭代器失效问题

最近在项目开发中,遇到一个异常,经过测试,发现是迭代器失效问题,于是稍微总结了一下. vector迭代器失效测试: 测试程序: void vectorTest(){    vector<int> container;    for (int i = 0; i < 10; i++)    {        container.push_back(i);    } vector<int>::iterator iter;     for (iter = container.begi

C++之迭代器失效总结

1. 对于序列式容器(如vector,deque),序列式容器就是数组式容器,删除当前的iterator会使后面所有元素的iterator都失效.这是因为vetor,deque使用了连续分配的内存,删除一个元素导致后面所有的元素会向前移动一个位置.所以不能使用erase(iter++)的方式,还好erase方法可以返回下一个有效的iterator. for (iter = cont.begin(); iter != cont.end();) { (*it)->doSomething(); if

c++之迭代器失效

1.首先从一到题目开始谈说起迭代器失效.有时我们很自然并且自信地 用下面方法删除vector元素: #include <iostream>#include <stdio.h>#include <vector>#include <algorithm>#include <string> void del_elem(vector<string> &vec, const char * elem) { vector<string&

C++中防止STL中迭代器失效——map/set等关联容器——vector/list/deque等序列容器—如何防止迭代器失效—即erase()的使用

序列性容器::(vector和list和deque) erase迭代器不仅使所有指向被删元素的迭代器失效,而且使被 删元素之后的所有迭代器失效,所以不能使用erase(iter++)的方 式,但是erase的返回值为下一个有效的迭代器,所以   正确方法为:: for( iter = c.begin(); iter != c.end(); ) iter = c.erase(iter); 关联性容器::(map和set比较常用) erase迭代器只是被删元素的迭代器失效,但是返回值为void, 所

vector和map的迭代器失效问题

1.vector #include <iostream> #include <string> #include <vector> using namespace std; void vectorTest() { vector<int> container; for (int i = 0; i < 10; i++) { container.push_back(i); } vector<int>::iterator iter; for (ite

vector迭代器失效的几种情况

在泛型编程还是STL的实际运用中,迭代器(iterator)无疑扮演者重要的角色.迭代器是一种类似于指针的对象(如可以内容提领,成员访问等),但他又不仅仅是一种普通的指针.关于迭代器失效,我们可以看下面这个例子: #include<vector>#include<list>void PrintVector(const vector<int>& v){    vector<int>::const_iterator it = v.begin();    

vector迭代器失效的一种情形

使用过STL的人都应该知道关于迭代器失效的原理,这里以后vector迭代器失效为例: 第一种:当插入一个元素到vector中,如果插入后容器已满,那么容器将新开辟一块内存区域,然后 将原内存中的数据拷贝到新的内存区域,同时释放旧的内存.这样之前指向旧内存的迭代器就会指向 不确定内存,这块内存要么释放,要么释放后又用作其他用途.这便导致了迭代器失效. 第二种:当删除容器中一个元素后,该迭代器所指向的元素已经被删除,那么也造成迭代器失效. 这里我们主要讨论下第二种情况. 我们先举个例子说明: 比如v

STL源码分析--迭代器总结、迭代器失效总结

Vector 1.内部数据结构:连续存储,例如数组. 2.随机访问每个元素,所需要的时间为常量. 3.在末尾增加或删除元素所需时间与元素数目无关,在中间或开头增加或删除元素所需时间随元素数目呈线性变化. 4.可动态增加或减少元素,内存管理自动完成,但程序员可以使用reserve()成员函数来管理内存. 5.迭代器失效 插入:vector的迭代器在内存重新分配时将失效(它所指向的元素在该操作的前后不再相同).当把超过capacity()-size()个元素插入vector中时,内存会重新分配,所有