【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底層實現是由紅黑樹實現的,所以當刪除一個元素時,會進行二叉樹的調整,但每個節點在內存中的地址是沒有改變的,改變的只是他們之間的指針指向。

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