c++迭代器失效的情況匯總
一、序列式容器(數(shù)組式容器)
對于序列式容器(如vector,deque),序列式容器就是數(shù)組式容器,刪除當前的iterator會使后面所有元素的iterator都失效。這是因為vetor,deque使用了連續(xù)分配的內(nèi)存,刪除一個元素導致后面所有的元素會向前移動一個位置。所以不能使用erase(iter++)的方式,還好erase方法可以返回下一個有效的iterator。
for (iter = cont.begin(); iter != cont.end();) { (*it)->doSomething(); if (shouldDelete(*iter)) iter = cont.erase(iter); //erase刪除元素,返回下一個迭代器 else ++iter; }
迭代器失效:
void vectorTest() { vector<int> container; for (int i = 0; i < 10; i++) { container.push_back(i); } vector<int>::iterator iter; for (iter = container.begin(); iter != container.end(); iter++) { if (*iter > 3) container.erase(iter); } for (iter = container.begin(); iter != container.end(); iter++) { cout<<*iter<<endl; } }
報錯是:vectoriterator not incrementable.
迭代器在執(zhí)行++操作時報錯!已經(jīng)失效的迭代器不能再進行自增運算了。++代碼大致實現(xiàn)如下:
_Myiter operator++(int) { _Myiter _Tmp=*this; ++*this; return (_Tmp); }
對于序列式容器,比如vector,刪除當前的iterator會使后面所有元素的iterator都失效。這是因為順序容器內(nèi)存是連續(xù)分配(分配一個數(shù)組作為內(nèi)存),刪除一個元素導致后面所有的元素會向前移動一個位置。(刪除了一個元素,該元素后面的所有元素都要挪位置,所以,iter++,已經(jīng)指向的是未知內(nèi)存)。
但是erase方法可以返回下一個有效的iterator。所以代碼做如下修改,就OK了。
void vectorTest() { vector<int> container; for (int i = 0; i < 10; i++) { container.push_back(i); } vector<int>::iterator iter; for (iter = container.begin(); iter != container.end();) { if (*iter > 3) { iter = container.erase(iter); } else { iter ++; } } for (iter = container.begin(); iter != container.end(); iter++) { cout<<*iter<<endl; } }
總結(jié):vector是一個順序容器,在內(nèi)存中是一塊連續(xù)的內(nèi)存,當刪除一個元素后,內(nèi)存中的數(shù)據(jù)會發(fā)生移動,以保證數(shù)據(jù)的緊湊。所以刪除一個數(shù)據(jù)后,其他數(shù)據(jù)的地址發(fā)生了變化,之前獲取的迭代器根據(jù)原有的信息就訪問不到正確的數(shù)據(jù)。
所以為了防止vector迭代器失效,常用如下方法:
for (iter = container.begin(); iter != container.end(); ) { if (*iter > 3) iter = container.erase(iter); //erase的返回值是刪除元素下一個元素的迭代器 else{ iter++; } }
這樣刪除后iter指向的元素后,返回的是下一個元素的迭代器,這個迭代器是vector內(nèi)存調(diào)整過后新的有效的迭代器。
二、關(guān)聯(lián)式容器
對于關(guān)聯(lián)容器(如map, set,multimap,multiset),刪除當前的iterator,僅僅會使當前的iterator失效,只要在erase時,遞增當前iterator即可。這是因為map之類的容器,使用了紅黑樹來實現(xiàn),插入、刪除一個結(jié)點不會對其他結(jié)點造成影響。erase迭代器只是被刪元素的迭代器失效,但是返回值為void,所以要采用erase(iter++)的方式刪除迭代器。
for (iter = cont.begin(); it != cont.end();) { (*iter)->doSomething(); if (shouldDelete(*iter)) cont.erase(iter++); else ++iter; } //測試錯誤的Map刪除元素 void mapTest() { map<int, string> dataMap; for (int i = 0; i < 100; i++) { string strValue = "Hello, World"; stringstream ss; ss<<i; string tmpStrCount; ss>>tmpStrCount; strValue += tmpStrCount; dataMap.insert(make_pair(i, strValue)); } cout<<"MAP元素內(nèi)容為:"<<endl; map<int, string>::iterator iter; for (iter = dataMap.begin(); iter != dataMap.end(); iter++) { int nKey = iter->first; string strValue = iter->second; cout<<strValue<<endl; } cout<<"內(nèi)容開始刪除:"<<endl; //刪除操作引發(fā)迭代器失效 for (iter = dataMap.begin(); iter != dataMap.end();iter++) { int nKey = iter->first; string strValue = iter->second; if (nKey % 2 == 0) { dataMap.erase(iter); //錯誤 } /* cout<<iter->second<<endl;*/ } }
出錯:
解析:dataMap.erase(iter)之后,iter就已經(jīng)失效了,所以iter無法自增,即iter++就會出bug.解決方案,就是在iter失效之前,先自增。
void mapTest() { map<int, string> dataMap; for (int i = 0; i < 100; i++) { string strValue = "Hello, World"; stringstream ss; ss<<i; string tmpStrCount; ss>>tmpStrCount; strValue += tmpStrCount; dataMap.insert(make_pair(i, strValue)); } cout<<"MAP元素內(nèi)容為:"<<endl; map<int, string>::iterator iter; for (iter = dataMap.begin(); iter != dataMap.end(); iter++) { int nKey = iter->first; string strValue = iter->second; cout<<strValue<<endl; } cout<<"內(nèi)容開始刪除:"<<endl; for (iter = dataMap.begin(); iter != dataMap.end();) { int nKey = iter->first; string strValue = iter->second; if (nKey % 2 == 0) { dataMap.erase(iter++); auto a = iter; } else { iter ++; } } }
解析:dataMap.erase(iter++);這句話分三步走,先把iter傳值到erase里面,然后iter自增,然后執(zhí)行erase,所以iter在失效前已經(jīng)自增了。
map是關(guān)聯(lián)容器,以紅黑樹或者平衡二叉樹組織數(shù)據(jù),雖然刪除了一個元素,整棵樹也會調(diào)整,以符合紅黑樹或者二叉樹的規(guī)范,但是單個節(jié)點在內(nèi)存中的地址沒有變化,變化的是各節(jié)點之間的指向關(guān)系。
所以在map中為了防止迭代器失效,在有刪除操作時,常用如下方法:
for (iter = dataMap.begin(); iter != dataMap.end(); ) { int nKey = iter->first; string strValue = iter->second; if (nKey % 2 == 0) { map<int, string>::iterator tmpIter = iter; iter++; dataMap.erase(tmpIter); //dataMap.erase(iter++) 這樣也行 }else { iter++; } }
三、鏈表式容器
對于鏈表式容器(如list),刪除當前的iterator,僅僅會使當前的iterator失效,這是因為list之類的容器,使用了鏈表來實現(xiàn),插入、刪除一個結(jié)點不會對其他結(jié)點造成影響。只要在erase時,遞增當前iterator即可,并且erase方法可以返回下一個有效的iterator。
方式一:遞增當前iterator
for (iter = cont.begin(); it != cont.end();) { (*iter)->doSomething(); if (shouldDelete(*iter)) cont.erase(iter++); else ++iter; }
方式二:通過erase獲得下一個有效的iterator
for (iter = cont.begin(); iter != cont.end();) { (*it)->doSomething(); if (shouldDelete(*iter)) iter = cont.erase(iter); //erase刪除元素,返回下一個迭代器 else ++iter; }
四、總結(jié)
迭代器失效分三種情況考慮,也是分三種數(shù)據(jù)結(jié)構(gòu)考慮,分別為數(shù)組型,鏈表型,樹型數(shù)據(jù)結(jié)構(gòu)。
數(shù)組型數(shù)據(jù)結(jié)構(gòu):該數(shù)據(jù)結(jié)構(gòu)的元素是分配在連續(xù)的內(nèi)存中,insert和erase操作,都會使得刪除點和插入點之后的元素挪位置,所以,插入點和刪除掉之后的迭代器全部失效,也就是說insert(*iter)(或erase(*iter)),然后在iter++,是沒有意義的。解決方法:erase(*iter)的返回值是下一個有效迭代器的值。 iter =cont.erase(iter);
鏈表型數(shù)據(jù)結(jié)構(gòu):對于list型的數(shù)據(jù)結(jié)構(gòu),使用了不連續(xù)分配的內(nèi)存,刪除運算使指向刪除位置的迭代器失效,但是不會失效其他迭代器.解決辦法兩種,erase(*iter)會返回下一個有效迭代器的值,或者erase(iter++).
樹形數(shù)據(jù)結(jié)構(gòu): 使用紅黑樹來存儲數(shù)據(jù),插入不會使得任何迭代器失效;刪除運算使指向刪除位置的迭代器失效,但是不會失效其他迭代器.erase迭代器只是被刪元素的迭代器失效,但是返回值為void,所以要采用erase(iter++)的方式刪除迭代器。
注意:經(jīng)過erase(iter)之后的迭代器完全失效,該迭代器iter不能參與任何運算,包括iter++,*ite
以上就是c++迭代器失效的情況匯總的詳細內(nèi)容,更多關(guān)于c++迭代器失效的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
函數(shù)外初始化與函數(shù)內(nèi)初始化詳細解析
函數(shù)內(nèi)初始化:bool FillStr(char *&szDst, int nSize);第一個參數(shù)中的&一定不能少,這是因為在函數(shù)外部我們只聲明了這個指針,具體這個指針指向內(nèi)存中的哪個地址我們并不知道,所以&是為了說明傳遞的是這個指針的引用,那么在函數(shù)內(nèi)初始化后這個指針的地址也就是外面指針的地址了2013-09-09OpenCV中C++函數(shù)imread讀取圖片的問題及解決方法
利用C++函數(shù)imread讀取圖片的時候返回的結(jié)果總是空,而利用C函數(shù)cvLoadImage時卻能讀取到圖像。怎么回事?今天小編通過本教程給大家簡單說明原因2017-03-03C++編程之CString、string與、char數(shù)組的轉(zhuǎn)換
這篇文章主要介紹了C++編程之CString、string與、char數(shù)組的轉(zhuǎn)換的相關(guān)資料,希望通過本文能幫助到大家,讓大家學習理解這部分內(nèi)容,需要的朋友可以參考下2017-10-10C語言數(shù)據(jù)結(jié)構(gòu)之二叉樹詳解
二叉樹(Binary tree)是樹形結(jié)構(gòu)的一個重要類型。許多實際問題抽象出來的數(shù)據(jù)結(jié)構(gòu)往往是二叉樹形式。本文將通過示例詳細講解一下二叉樹,需要的可以參考一下2022-03-03QT中QByteArray與char、int、float之間的互相轉(zhuǎn)化
本文主要介紹了QT中QByteArray與char、int、float之間的互相轉(zhuǎn)化,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2022-05-05C++實現(xiàn)LeetCode(187.求重復(fù)的DNA序列)
這篇文章主要介紹了C++實現(xiàn)LeetCode(187.求重復(fù)的DNA序列),本篇文章通過簡要的案例,講解了該項技術(shù)的了解與使用,以下就是詳細內(nèi)容,需要的朋友可以參考下2021-07-07