C++ 迭代器失效问题
今天同学华为面试被问到vector有什么问题了,我一拍脑门,vector有什么问题??
原来是迭代器失效问题。先看看vector中:
void test_vector_erase(){ vector<int> v = {1,2,3,4,5}; for(auto it = v.begin(); it != v.end(); it++){ if(*it == 3) v.erase(it); } for(int& x : v) cout<<x<<" "; }
我本来是想以这个例子举例的,但似乎没有bug??给我整不会了。
但是我又分别测试了删除 1 和删除 5 的情况,答案是删除5的时候会出问题
void test_vector_erase(){ vector<int> v = {1,2,3,4,5}; for(auto it = v.begin(); it != v.end(); it++){ if(*it == 5) //会出问题, 这里改成*it > 2也会出问题 v.erase(it); } for(int& x : v) cout<<x<<" "; }
其实erase操作之后it还是会留在原位置,比如1,2,3,我们删除了2,变成了1,3,it由原来指向变成了指向3,这个时候做--操作,抵消for循环里面的++操作可以得到正确的结果
void modify_vector_erase(){ vector<int> v = {1,2,3,4,5}; for(auto it = v.begin(); it != v.end(); it++){ if(*it > 2) { it = v.erase(it); it--; } } for(auto it = v.begin(); it != v.end(); it++){ cout<<*it<<" "; } }
再来看看insert操作是如何让迭代器失效的
int test_vector_insert(){ vector<int> v(3); v.reserve(10); //注意这句话, 不加这句话输出的结果是99 98 0 0 100 3 98 ,可以看出迭代器并不在我们想要的位置,因为insert当空间不够时会引起扩容,原数组被搬到了另一块更大的空间,迭代器还留在原来的位置
//加上这句话,也就是vector的预分配函数后,我们insert四个数不会引起扩容,因此会打印出正确结果 3 2 1 0 100 99 98
v[0] = 100;v[1] = 99, v[2] = 98; auto it = v.begin(); v.insert(it, 1, 0); v.insert(it, 1, 1); v.insert(it, 1, 2); v.insert(it, 1, 3); for(auto it = v.begin(); it != v.end(); it++){ cout<<*it<<" "; } return 0; }
再看看关联性容器map, 对于map和list,这种存放位置不连续的容器,当客户端对它进行元素新增操作(insert)或者删除(erase)操作时,操作之前的所有迭代器在操作完成后都依然有效,被删除的那个元素的迭代器是个例外。
首先直接删除任意一个元素后就不会继续遍历后面的元素了,输出1,3,4,5。被删除的it被删除后还是指向3,而此时it的下一个是mp.end()
void test_map_erase(){ map<int, char> mp{{1,'a'},{2,'b'},{3,'c'},{4,'d'},{5,'e'}}; for(auto it = mp.begin(); it != mp.end(); it++){ if(it -> first > 2){
mp.erase(it);
cout<<it->first<<endl;
} } for(auto it = mp.begin(); it != mp.end(); it++) cout<<it->first<<" "; }
下面这样可以完整删除3,4,5
void test_map_erase(){ map<int, char> mp{{1,'a'},{2,'b'},{3,'c'},{4,'d'},{5,'e'}}; for(auto it = mp.begin(); it != mp.end(); it++){ if(it -> first > 2) { mp.erase(it++); it--; } } for(auto it = mp.begin(); it != mp.end(); it++) cout<<it->first<<" "; }
总之,我觉得不会有哪个傻蛋会一遍修改容器一边遍历容器吧!
另外erase操作和remove操作也是有区别的
//remove的官方解释,是把一个范围内的某个数删除,但是并没有释放容器空间 template <class ForwardIterator, class T> ForwardIterator remove (ForwardIterator first, ForwardIterator last, const T& val) { ForwardIterator result = first; while (first!=last) { if (!(*first == val)) { if (result!=first) *result = move(*first); ++result; } ++first; } return result; } //vector 中的 erase是删除某个位置并且释放了空间的(ps:map的源码我没看到,不知道有没有释放) iterator erase(iterator position){ if(position + 1 != end()) copy(position + 1, finish, position); --finish; //finish是vector中已使用空间的尾,end_of_storage 是vector可使用空间的尾 destroy(finish); return position; }
STL共有这五种类型的迭代器,迭代器其实是一种行为类似智能指针的对象。