轉載地址:http://www.cnblogs.com/xkfz007/articles/2509433.html
最近在項目開發中,遇到一個異常,經過測試,發現是迭代器失效問題,於是稍微總結了一下。
vector迭代器失效測試:
測試程序:
{
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;
}
}
結果對嗎?肯定不對,結果應該是0,1,2,3。
在看看跟進去之後的報錯情況:
迭代器在執行++操作時報錯!
對於序列式容器,比如vector,刪除當前的iterator會使後面所有元素的iterator都失效。這是因爲順序容器內存是連續分配,刪除 一個元素導致後面所有的元素會向前移動一個位置。但是erase方法可以返回下一個有效的iterator。所以代碼做如下修改,就OK了。
{
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); //erase的返回值是刪除元素下一個元素的迭代器
else{
iter++;
}
}
for (iter = container.begin(); iter != container.end(); iter++)
{
cout<<*iter<<endl;
}
}
運行結果如下:
結果是正確的。
再看一個MAP的示例,
{
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元素內容爲:"<<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<<"內容開始刪除:"<<endl;
/////////////////////////////////////////////擦除操作引發迭代器失效
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;
}
}
結果報錯:
根據迭代器訪問數據時失效! map/set iterator not dereferencable
迭代器是不引用的。
正確的做法是:
{
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元素內容爲:"<<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<<"內容開始刪除:"<<endl;
for (iter = dataMap.begin(); iter != dataMap.end(); )
{
int nKey = iter->first;
string strValue = iter->second;
if (nKey % 2 == 0)
{
dataMap.erase(iter++);
}else
{
iter++;
}
if (iter != dataMap.end())
cout<<iter->second<<endl;
}
最後輸出結果:
總結一下:
vector是一個順序容器,在內存中是一塊連續的內存,當刪除一個元素後,內存中的數據會發生移動,以保證數據的緊湊。所以刪除一個數據後,其他數據的地址發生了變化,之前獲取的迭代器根據原有的信息就訪問不到正確的數據。
所以爲了防止vector迭代器失效,常用如下方法:
{
if (*iter > 3)
iter = container.erase(iter); //erase的返回值是刪除元素下一個元素的迭代器
else{
iter++;
}
}
這樣刪除後iter指向的元素後,返回的是下一個元素的迭代器,這個迭代器是vector內存調整過後新的有效的迭代器。萬無一失!
map是關聯容器,以紅黑樹或者平衡二叉樹組織數據,雖然刪除了一個元素,整棵樹也會調整,以符合紅黑樹或者二叉樹的規範,但是單個節點在內存中的地址沒有變化,變化的是各節點之間的指向關係。
所以在map中爲了防止迭代器失效,在有刪除操作時,常用如下方法:
{
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++;
}
}
其中,
map<int, string>::iterator tmpIter = iter; iter++;
dataMap.erase(tmpIter);
這幾句的意思是,先保留要刪除的節點迭代器,再讓iter向下一個有意義的節點,然後刪除節點。
所以這個操作結束後iter指向的是下一個有意義的節點,沒有失效。
其實這三句話可以用在一句話代替,就是dataMap.erase(iter++);
解釋是先讓iter指向下一個有效的節點,但是返回給erase函數的是原來的iter副本。這個可能跟++這個操作的本身語法相關。
但是功能跟上面是一樣的。