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共有这五种类型的迭代器,迭代器其实是一种行为类似智能指针的对象。

       

 

posted @ 2021-09-14 16:19  鳄鱼四驱车  阅读(179)  评论(0编辑  收藏  举报