c++迭代器失效的情況匯總
一、序列式容器(數(shù)組式容器)
對于序列式容器(如vector,deque),序列式容器就是數(shù)組式容器,刪除當(dāng)前的iterator會使后面所有元素的iterator都失效。這是因?yàn)関etor,deque使用了連續(xù)分配的內(nèi)存,刪除一個(gè)元素導(dǎo)致后面所有的元素會向前移動一個(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);
}
對于序列式容器,比如vector,刪除當(dāng)前的iterator會使后面所有元素的iterator都失效。這是因?yàn)轫樞蛉萜鲀?nèi)存是連續(xù)分配(分配一個(gè)數(shù)組作為內(nèi)存),刪除一個(gè)元素導(dǎo)致后面所有的元素會向前移動一個(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ù)會發(fā)生移動,以保證數(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)式容器
對于關(guān)聯(lián)容器(如map, set,multimap,multiset),刪除當(dāng)前的iterator,僅僅會使當(dāng)前的iterator失效,只要在erase時(shí),遞增當(dāng)前iterator即可。這是因?yàn)閙ap之類的容器,使用了紅黑樹來實(shí)現(xiàn),插入、刪除一個(gè)結(jié)點(diǎn)不會對其他結(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++就會出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è)元素,整棵樹也會調(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++;
}
}
三、鏈表式容器
對于鏈表式容器(如list),刪除當(dāng)前的iterator,僅僅會使當(dāng)前的iterator失效,這是因?yàn)閘ist之類的容器,使用了鏈表來實(shí)現(xiàn),插入、刪除一個(gè)結(jié)點(diǎn)不會對其他結(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操作,都會使得刪除點(diǎn)和插入點(diǎn)之后的元素挪位置,所以,插入點(diǎn)和刪除掉之后的迭代器全部失效,也就是說insert(*iter)(或erase(*iter)),然后在iter++,是沒有意義的。解決方法:erase(*iter)的返回值是下一個(gè)有效迭代器的值。 iter =cont.erase(iter);
鏈表型數(shù)據(jù)結(jié)構(gòu):對于list型的數(shù)據(jù)結(jié)構(gòu),使用了不連續(xù)分配的內(nèi)存,刪除運(yùn)算使指向刪除位置的迭代器失效,但是不會失效其他迭代器.解決辦法兩種,erase(*iter)會返回下一個(gè)有效迭代器的值,或者erase(iter++).
樹形數(shù)據(jù)結(jié)構(gòu): 使用紅黑樹來存儲數(shù)據(jù),插入不會使得任何迭代器失效;刪除運(yùn)算使指向刪除位置的迭代器失效,但是不會失效其他迭代器.erase迭代器只是被刪元素的迭代器失效,但是返回值為void,所以要采用erase(iter++)的方式刪除迭代器。
注意:經(jīng)過erase(iter)之后的迭代器完全失效,該迭代器iter不能參與任何運(yùn)算,包括iter++,*ite
以上就是c++迭代器失效的情況匯總的詳細(xì)內(nèi)容,更多關(guān)于c++迭代器失效的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
QT利用QPdfWriter實(shí)現(xiàn)繪制PDF(支持表單輸出)
這篇文章主要為大家詳細(xì)介紹了QT如何利用QPdfWriter實(shí)現(xiàn)繪制PDF,并可以支持表單輸出。文中的示例代碼講解詳細(xì),感興趣的小伙伴可以了解一下2023-01-01
利用Matlab實(shí)現(xiàn)時(shí)域分析功能的示例詳解
利用MATLAB可以方便地進(jìn)行控制系統(tǒng)的時(shí)域分析。這篇文章主要通過簡單的示例為大家介紹了Matlab進(jìn)行時(shí)域分析的具體操作,需要的可以參考一下2023-02-02
c++ 類函數(shù)作為模板參數(shù)實(shí)現(xiàn)方式詳解
這篇文章主要介紹了c++ 類函數(shù)作為模板參數(shù)實(shí)現(xiàn)方式,在實(shí)現(xiàn)中加入增強(qiáng)邏輯,這種方式對代碼侵入性過高,而且無法控制該邏輯是否需要,如果不需要的話又得重新修改代碼實(shí)現(xiàn),需要的朋友可以參考下2023-03-03
C語言實(shí)現(xiàn)學(xué)生獎(jiǎng)學(xué)金評定系統(tǒng)
這篇文章主要介紹了C語言實(shí)現(xiàn)學(xué)生獎(jiǎng)學(xué)金評定系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-03-03
C語言數(shù)據(jù)結(jié)構(gòu)之二叉樹的非遞歸后序遍歷算法
這篇文章主要介紹了C語言數(shù)據(jù)結(jié)構(gòu)之二叉樹的非遞歸后序遍歷算法的相關(guān)資料,希望通過本文能幫助到大家,讓大家實(shí)現(xiàn)這樣的功能,需要的朋友可以參考下2017-10-10

