迭代器是C++中非常重要的概念,是STL工具集中的重要一環。迭代器有很多類型,各自有其侷限性。
-
輸入迭代器:只能取指向的值,當迭代器自加後,之前指向的值就不可訪問(不用此類迭代器在一個範圍內遍歷多次),典型的如std::istream_iterator。
-
前向迭代器:類似輸入迭代器只能前進,但在指定範圍內可迭代多次,典型的如std::forward_list(只能前向遍歷,但可以反覆迭代)。
-
輸出迭代器:單純用於寫的迭代器,只能前進,且將內容寫入對應容器(文件)中;讀取的值是未定義的。
迭代器基礎
每種容器都定義了自己的迭代器,可通過iterator獲取(如vector::iterator)。通過begin與end函數可獲取一對迭代器,表示範圍(end不指向容器的任何元素,而是指向最後元素的下一個位置)以遍歷容器元素。
vector<int> vec;
// ...
for(auto it=vec.begin(); it!=vec.end(); ++it){
cout<<*it<<endl;
}
// C++11
for(auto it:vec){
cout<<*it<<endl;
}
基本操作
迭代器可以像指針一樣進行++與--操作,以及通過*解除引用,和比較操作等。此外還有三個函數模板:
-
advance(p,n):迭代器向前或向後(負數)移動n個元素;
-
distance(p,q):計算兩個迭代器間的距離;
-
iter_swap(p,q):交換兩個迭代器;
高級使用
迭代器與STL中的算法結合,可以產生強大的威力;通過插入迭代器,可方便插入數據至容器:
-
inserter(con,it):從容器con的it處開始插入
-
back_inserter(con):插入到容器尾部(需要容器支持push_back);
-
front_inserter(con):插入到容器頭部(需要容器支持push_front);
通過copy輸出容器內容:
template<typename T>
void toPrint(const T &con){
copy(begin(con),end(con), ostream_iterator<T::value_type>{count, ", "});
cout<<endl;
}
使用inserter創建容器
vector<int> vec{1,2,3,4,5};
deque<int> de;
copy_n(begin(vec), 3, inserter(de,begin(de))); // 從指定位置往後,依次插入內容
move(begin(vec),end(vec), front_inserter(de)); // 總是插入最前面,若是可移動類型(如string等)則vec中的內容將變爲空
toPrint(de); // 5,4,3,2,1,1,2,3
算法中應用
在算法中會大量使用迭代器進行操作。
二分查找
對於已排序的容器,可通過查找算法獲取對應位置迭代器:
-
binary_search(begin,end,foFind):查找到返回true,否則false;
-
equal_range(begin,end,foFind):返回一對迭代器(pair<lowerBound,UpperBound>),符合要求的元素爲[lowerBound,UpperBound),若未查找到則lowerBound==UpperBound(即都指向符合要求元素插入的位置);
-
lower_bound(begin,end,foFind):返回第一個不小於(less)查找值的迭代器,若未找到返回end;
-
upper_bound(begin,end,foFind):返回大於查找值的第一個元素位置(即新元素插入位置);
排序
STL中有多個排序算法,滿足不同用途:
-
sort:排序
-
stable_sort:穩定排序
-
partial_sort(first, middle, last):保證[first,middle)範圍內容的元素是排序好的,且不大於後面的任何元素;
-
partition(first,last,pre):返回第一個不滿足pre的迭代器it,保證[first,it)範圍內元素滿足pre,[it,last)範圍內元素不滿足pre;
-
nth_element(first, itN, last):把第N個元素放在位置N處,且前面的元素不大於它,後面的元素不小於它(但不排序);
vector<int> vec{1,2,3,4,5,6,7,8,9};
default_random_engine gbump;
shuffle(vec.begin(),vec.end(),g);
auto mid{next(vec.begin(), vec.size()/3)};
// partial_sort(vec.begin(), mid, vec.end()); // 保證前1/3元素有序
auto ret = parition(vec.begin(),vec.end(), [](auto i){return i<5}); // 所有小於5的排在前面,其他排在後面
cout *ret<<endl;
toPrint(vec);
創建迭代器
要實現一個前向迭代器,只需支持前綴加++,解引用*和比較操作!=即可(可用於enumeration-for語句中)。
class Num_iterator{
int m_cur=0;
public:
explicit Num_iterator{int nPos=0):m_cur(nPos){}
int operator*() {return m_cur;}
Num_iterator& operator++(){
++m_cur;
return *this;
}
Num_iterator& operator--(){
--m_cur;
return *this;
}
bool operator!=(const Num_iterator& other){
return m_cur != other.m_cur;
}
bool operator==(const Num_iterator& other){
return !operator!=(other);
}
};
爲了使迭代器兼容STL算法,需要對標準模板進行特化:
template<>
struct iterator_traits<Num_iterator>{
using iterator_category = std::forward_iterator_tag;
using value_type = int;
using difference_type = int;
using pointer = const int*;
using reference = int;
}
}
有了迭代器,我們就可以定義自己的容器,如獲取數字範圍:
class Num_range{
int m_nStart=0;
int m_nEnd=0;
public:
Num_range{int from, int to):m_nStart(from), m_nEnd(to) {}
Num_iterator begin() const { return Num_iterator(m_nStart);}
Num_iterator end() const {return Num_iterator(m_nEnd);}
Num_iterator begin() { return Num_iterator(m_nStart);}
Num_iterator end() {return Num_iterator(m_nEnd);}
};
使用自定義容器:
Num_range rg(1, 10);
vector<int> vec;
transform(rg.begin(), rg.end(), back_inserter(vec), [](auto i) {return i*i;});