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