前言
關於面試時有被問到過這類問題,當時由於只一知半解,回答的不是特別好,所以今天自己特意來實驗一下。希望能幫助大家有同樣疑惑的人解答疑惑!
目錄
關於迭代器失效的幾種情況
迭代器(iterator)類似於指針的效果,比如有自加(it++)、解引用(*it)的效果。對於vector容器,它的底層就是一段連續的空間,所以對於以上兩種效果很容易滿足,即在vector內部就是一個普通的指針。而對於其他的容器,比如鏈表(list),它的底層不是一段連續的空間,所以對於自加(it++)這種效果是不能用一個普通指針來進行實現的,所以它把iterator封裝成了一個類,在類中對自加(it++)這種操作符進行運算符重載operator ++(),讓大家進行迭代器操作時,看似和指針一樣的效果。這是關於迭代器的兩種不同的實現形式,當然內部還有很多細節就不一一介紹了,大家想了解更多,可以查閱侯捷老師的《STL源碼剖析》這本書。
1、序列式容器迭代器失效【vector】
失效情況總結:
- 當使用erase方法後:指向刪除節點後的迭代器全部失效
- 當使用push_back方法後:end操作返回的迭代器肯定失效,關於end之前的迭代器則可能失效,也可能不會失效,若push_back操作後,capacity沒有變化,則不會失效,如果有變化則會失效。
使用erase
#include<iostream>
#include<vector>
using namespace std;
int main(){
vector<int> q{1,2,3,4,5,6};
//在這裏想把大於2的元素都刪除
for(auto it=q.begin();it!=q.end();it++){
if(*it>2)
q.erase(it);//這裏就會發生迭代器失效
}
//打印結果
for(auto it=q.begin();it!=q.end();it++){
cout<<*it<<" ";
}
cout<<endl;
return 0;
}
解釋:對於erase操作後,會返回下一個元素的迭代器,所以已經進行了一次自加的效果了,而循環體每次也在進行自加,所以有時候會是兩次自加的效果,因此發生了這種現象。
解決方法如下:
//在這裏想把大於2的元素都刪除
for(auto it=q.begin();it!=q.end();)
{
if(*it>2)
{
it=q.erase(it);//這裏會返回指向下一個元素的迭代器,因此不需要再自加了
}
else
{
it++;
}
}
使用push_back
(1)不會使前面的迭代器失效
#include<iostream>
#include<vector>
using namespace std;
int main(){
vector<int> q;
q.push_back(1);
q.push_back(2);
q.push_back(3);
cout<<"原容器容量:"<<q.capacity()<<endl;
auto it_start=q.begin(),it_end=q.end();
q.push_back(4);//利用push_back添加一個元素
cout<<"push_back後容器容量:"<<q.capacity()<<endl;
cout<<"利用原先迭代器進行遍歷容器結果:";
while(it_start!=it_end){
cout<<*it_start<<" ";
it_start++;
}
cout<<endl;
cout<<"利用更新後迭代器進行遍歷容器結果:";
for(auto it=q.begin();it!=q.end();it++){
cout<<*it<<" ";
}
cout<<endl;
return 0;
}
解釋:我們可以發現,當容器的容量capacity前後沒有改變時,只會使end操作符返回的迭代器失效,而之前的迭代器並沒有失效。這是因爲capacity沒有擴充,那麼就是在原先分配的內存上繼續進行添加元素,所以前面的迭代器不會失效。
(2)會使前面的迭代器也失效
#include<iostream>
#include<vector>
using namespace std;
int main(){
vector<int> q;
q.push_back(1);
q.push_back(2);
q.push_back(3);
q.push_back(4);
cout<<"原容器容量:"<<q.capacity()<<endl;
auto it_start=q.begin(),it_end=q.end();
q.push_back(5);//利用push_back添加一個元素
cout<<"push_back後容器容量:"<<q.capacity()<<endl;
cout<<"利用原先迭代器進行遍歷容器結果:";
while(it_start!=it_end){
cout<<*it_start<<" ";
it_start++;
}
cout<<endl;
cout<<"利用更新後迭代器進行遍歷容器結果:";
for(auto it=q.begin();it!=q.end();it++){
cout<<*it<<" ";
}
cout<<endl;
return 0;
}
解釋:在這裏,我們會發現前後兩者的容量capacity不一樣,利用原先迭代器進行遍歷的結果,也和最後用更新後的迭代器進行遍歷結果的前4個元素值不一樣。這就是所有的迭代器都失效了,原因是因爲vector噹噹前容量不足時,會去另外一段空閒的內存開闢一個是原先容量兩倍大的內存,並把原先地方的值依次複製到新的內存中,原先內存即是空閒內存,可被任何程序使用,即是不確定的值,因此利用原先的迭代器進行遍歷,最後結果也是不確定的。
2、關聯式容器迭代器失效【map】
對於map,當進行erase操作後,只會使當前迭代器失效,不會造成其他迭代器失效,這是因爲map底層實現是由紅黑樹實現的,所以當刪除一個元素時,會進行二叉樹的調整,但每個節點在內存中的地址是沒有改變的,改變的只是他們之間的指針指向。