【C++知识】关于迭代器失效的几种情况

前言

       关于面试时有被问到过这类问题,当时由于只一知半解,回答的不是特别好,所以今天自己特意来实验一下。希望能帮助大家有同样疑惑的人解答疑惑!

目录

关于迭代器失效的几种情况

1、序列式容器迭代器失效【vector】

使用erase

使用push_back

 2、关联式容器迭代器失效【map】


关于迭代器失效的几种情况

       迭代器(iterator)类似于指针的效果,比如有自加(it++)、解引用(*it)的效果。对于vector容器,它的底层就是一段连续的空间,所以对于以上两种效果很容易满足,即在vector内部就是一个普通的指针。而对于其他的容器,比如链表(list),它的底层不是一段连续的空间,所以对于自加(it++)这种效果是不能用一个普通指针来进行实现的,所以它把iterator封装成了一个类,在类中对自加(it++)这种操作符进行运算符重载operator ++(),让大家进行迭代器操作时,看似和指针一样的效果。这是关于迭代器的两种不同的实现形式,当然内部还有很多细节就不一一介绍了,大家想了解更多,可以查阅侯捷老师的《STL源码剖析》这本书。

1、序列式容器迭代器失效【vector】

失效情况总结:

  • 当使用erase方法后:指向删除节点后的迭代器全部失效
  • 当使用push_back方法后:end操作返回的迭代器肯定失效,关于end之前的迭代器则可能失效,也可能不会失效,若push_back操作后,capacity没有变化,则不会失效,如果有变化则会失效。

使用erase

#include<iostream>
#include<vector>

using namespace std;

int main(){
    
    vector<int> q{1,2,3,4,5,6};
    //在这里想把大于2的元素都删除
    for(auto it=q.begin();it!=q.end();it++){
        if(*it>2)
            q.erase(it);//这里就会发生迭代器失效
    }
    //打印结果
    for(auto it=q.begin();it!=q.end();it++){
        cout<<*it<<" ";
    }
    cout<<endl;
    return 0;
}

解释:对于erase操作后,会返回下一个元素的迭代器,所以已经进行了一次自加的效果了,而循环体每次也在进行自加,所以有时候会是两次自加的效果,因此发生了这种现象。

解决方法如下:

//在这里想把大于2的元素都删除
    for(auto it=q.begin();it!=q.end();)
    {
        if(*it>2)
        {
            it=q.erase(it);//这里会返回指向下一个元素的迭代器,因此不需要再自加了
        }
        else
        {
            it++;
        }
    }

使用push_back

(1)不会使前面的迭代器失效

#include<iostream>
#include<vector>

using namespace std;

int main(){
    
    vector<int> q;
    q.push_back(1);
    q.push_back(2);
    q.push_back(3);
    
    cout<<"原容器容量:"<<q.capacity()<<endl;
    auto it_start=q.begin(),it_end=q.end();

    q.push_back(4);//利用push_back添加一个元素
    cout<<"push_back后容器容量:"<<q.capacity()<<endl;
    cout<<"利用原先迭代器进行遍历容器结果:";
    while(it_start!=it_end){
        cout<<*it_start<<" ";
        it_start++;
    }
    cout<<endl;
    
    cout<<"利用更新后迭代器进行遍历容器结果:";
    for(auto it=q.begin();it!=q.end();it++){
        cout<<*it<<" ";
    }
    cout<<endl;
    return 0;
}

 解释:我们可以发现,当容器的容量capacity前后没有改变时,只会使end操作符返回的迭代器失效,而之前的迭代器并没有失效。这是因为capacity没有扩充,那么就是在原先分配的内存上继续进行添加元素,所以前面的迭代器不会失效。

(2)会使前面的迭代器也失效

#include<iostream>
#include<vector>

using namespace std;

int main(){
    
    vector<int> q;
    q.push_back(1);
    q.push_back(2);
    q.push_back(3);
    q.push_back(4);
    
    cout<<"原容器容量:"<<q.capacity()<<endl;
    auto it_start=q.begin(),it_end=q.end();

    q.push_back(5);//利用push_back添加一个元素
    cout<<"push_back后容器容量:"<<q.capacity()<<endl;
    cout<<"利用原先迭代器进行遍历容器结果:";
    while(it_start!=it_end){
        cout<<*it_start<<" ";
        it_start++;
    }
    cout<<endl;
    
    cout<<"利用更新后迭代器进行遍历容器结果:";
    for(auto it=q.begin();it!=q.end();it++){
        cout<<*it<<" ";
    }
    cout<<endl;
    return 0;
}

 

解释:在这里,我们会发现前后两者的容量capacity不一样,利用原先迭代器进行遍历的结果,也和最后用更新后的迭代器进行遍历结果的前4个元素值不一样。这就是所有的迭代器都失效了,原因是因为vector当当前容量不足时,会去另外一段空闲的内存开辟一个是原先容量两倍大的内存,并把原先地方的值依次复制到新的内存中,原先内存即是空闲内存,可被任何程序使用,即是不确定的值,因此利用原先的迭代器进行遍历,最后结果也是不确定的。

 2、关联式容器迭代器失效【map】

      对于map,当进行erase操作后,只会使当前迭代器失效,不会造成其他迭代器失效,这是因为map底层实现是由红黑树实现的,所以当删除一个元素时,会进行二叉树的调整,但每个节点在内存中的地址是没有改变的,改变的只是他们之间的指针指向。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章