關於迭代器失效的幾種情況

之前就做題的時候就經常碰到與迭代器失效有關的問題,但是一直對這個問題也沒有深究,處於似懂非懂的狀態,今天就對迭代器失效這部分知識做一個總結。

迭代器

迭代器(iterator)是一個可以對其執行類似指針的操作(如:解除引用(operator*())和遞增(operator++()))的對象,我們可以將它理解成爲一個指針。但它又不是我們所謂普通的指針,我們可以稱之爲廣義指針,你可以通過sizeof(vector::iterator)來查看,所佔內存並不是4個字節。
如下圖所示:
這裏寫圖片描述
這裏我們定義了一個vector迭代器,對其求sizeof(),發現是12個字節,並不是一個指針的大小。

那麼我們常說的迭代器失效到底是什麼呢?都有哪些場景會導致失效問題呢?我們一起來看以下具體場景及解決辦法。

一、序列式容器迭代器失效

對於序列式容器,例如vector、deque;由於序列式容器是組合式容器,噹噹前元素的iterator被刪除後,其後的所有元素的迭代器都會失效,這是因爲vector,deque都是連續存儲的一段空間,所以當對其進行erase操作時,其後的每一個元素都會向前移一個位置。

#include<iostream>
using namespace std;

#include<vector>

void VectorTest()
{
    vector<int> vec;
    for (int i = 0; i < 5; i++)
    {
        vec.push_back(i);
    }
    vector<int>::iterator it;
    cout << sizeof(it) << endl;
    for (it = vec.begin(); it != vec.end(); it++)
    {
        if (*it>2)
            vec.erase(it);//此處會發生迭代器失效
    }
    for (it = vec.begin(); it != vec.end(); it++)
        cout << *it << " ";
    cout << endl;
}
int main()
{
    VectorTest();
    system("pause");
    return 0;
}

運行結果,程序終止:

這裏寫圖片描述
給出的報錯信息是:vector iterator not incrementable
已經失效的迭代器不能進行++操作,所以程序中斷了。不過vector的erase操作可以返回下一個有效的迭代器,所以只要我們每次執行刪除操作的時候,將下一個有效迭代器返回就可以順利執行後續操作了,代碼修改如下:

void VectorTest()
{
    vector<int> vec;
    for (int i = 0; i < 5; i++)
    {
        vec.push_back(i);
    }
    vector<int>::iterator it;
    cout << sizeof(it) << endl;
    for (it = vec.begin(); it != vec.end(); )
    {
        if (*it==3)
        {
            it = vec.erase(it);//更新迭代器it
        }
        it++;

    }
    for (it = vec.begin(); it != vec.end(); it++)
        cout << *it << " ";
    cout << endl;
}

運行結果:
這裏寫圖片描述
這樣刪除後it指向的元素後,返回的是下一個元素的迭代器,這個迭代器是vector內存調整過後新的有效的迭代器。此時就可以進行正確的刪除與訪問操作了。

上面只是舉了刪除元素造成的vector迭代器失效問題,對於vector的插入元素也可以同理得到驗證,這裏就不再進行舉例了。

vector迭代器失效問題總結

(1)當執行erase方法時,指向刪除節點的迭代器全部失效,指向刪除節點之後的全部迭代器也失效
(2)當進行push_back()方法時,end操作返回的迭代器肯定失效。
(3)當插入(push_back)一個元素後,capacity返回值與沒有插入元素之前相比有改變,則需要重新加載整個容器,此時first和end操作返回的迭代器都會失效。
(4)當插入(push_back)一個元素後,如果空間未重新分配,指向插入位置之前的元素的迭代器仍然有效,但指向插入位置之後元素的迭代器全部失效。

deque迭代器失效總結:

(1)對於deque,插入到除首尾位置之外的任何位置都會導致迭代器、指針和引用都會失效,但是如果在首尾位置添加元素,迭代器會失效,但是指針和引用不會失效
(2)如果在首尾之外的任何位置刪除元素,那麼指向被刪除元素外其他元素的迭代器全部失效
(3)在其首部或尾部刪除元素則只會使指向被刪除元素的迭代器失效。

二、關聯式容器迭代器失效

對於關聯容器(如map, set,multimap,multiset),刪除當前的iterator,僅僅會使當前的iterator失效,只要在erase時,遞增當前iterator即可。這是因爲map之類的容器,使用了紅黑樹來實現,插入、刪除一個結點不會對其他結點造成影響。erase迭代器只是被刪元素的迭代器失效,但是返回值爲void,所以要採用erase(iter++)的方式刪除迭代器。
首先來看一下map迭代器失效的一個例子:

void mapTest()
{
    map<int, int>m;
    for (int i = 0; i < 10; i++)
    {
        m.insert(make_pair(i, i + 1));
    }
    map<int, int>::iterator it;
    for (it = m.begin(); it != m.end(); it++)
    {
        if ((it->first)>5)
            m.erase(it);
    }

}
int main()
{
    mapTest();
    system("pause");
    return 0;
}

運行結果:
這裏寫圖片描述
這裏顯示迭代器失效,不能進行++ 操作,只要稍作修改就可以了:

    void mapTest()
{
    map<int, int>m;
    for (int i = 0; i < 10; i++)
    {
        m.insert(make_pair(i, i + 1));
    }
    map<int, int>::iterator it;

    for (it = m.begin(); it != m.end(); )
    {
        if (it->first==5)
            m.erase(it++);
        it++;
    }
    for (it = m.begin(); it != m.end();it++)
    {
        cout << (*it).first << " ";
    }
    cout << endl;
}

運行結果:
這裏寫圖片描述
此時就可以成功刪除key值爲5的元素了,而且迭代器++也沒有問題了。
這裏主要解釋一下erase(it++)的執行過程:這句話分三步走,先把iter傳值到erase裏面,然後iter自增,然後執行erase,所以iter在失效前已經自增了。
map是關聯容器,以紅黑樹或者平衡二叉樹組織數據,雖然刪除了一個元素,整棵樹也會調整,以符合紅黑樹或者二叉樹的規範,但是單個節點在內存中的地址沒有變化,變化的是各節點之間的指向關係。

發佈了115 篇原創文章 · 獲贊 74 · 訪問量 8萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章