7.1 迭代器頭文件
所有容器定義各自的迭代器類型,使用某容器迭代器時不需包含特定文件。
但有些迭代器,如逆向迭代器,被定義于頭文件<iterator>中。
7.2 iterator categories
7.2.1 input iterator
input iterator只能一次一個地向前讀取元素。
一旦input stream讀入一個字后,下次讀取時就返回另一個字。
使用前置運算性能更好。
7.2.2 output iterator
note:output iterator無comparison操作,無法檢驗output迭代器是否有效或“寫入”是否成功。
7.2.3 forward iterator
forward iterator是input iterator的全部功能和部分output iterator功能。
與input/output iterator不同,forward iterator能多次指向同一群集的同一元素,并能進(jìn)行多次處理。
output iterator不支持檢查是否到達(dá)序列尾端。
對forward 迭代器解引用時需確保其有效性。
7.2.4 bidirectional iterator
bidirectional iterator在forward iterator基礎(chǔ)上增加了對逆向遍歷的支持,即支持遞減操作符。
7.2.5 random access iterator
random access iterator在bidirectional iterator基礎(chǔ)上支持隨機(jī)存取。即支持“迭代器算術(shù)運算”。
以下對象/類型支持random access iterator:
- 可隨機(jī)存取的容器(vector/deque)
- string (字符串,string ,wstring)
-
一般array(指針)
t7-6.png
// 說明random access iterator的特殊方法
// iter/itercat.cpp
#include <vector>
#include <iostream>
using namespace std;
int main()
{
vector<int> col1;
// insert elements from -3 to 9
for (int i = -3; i <= 9; ++i)
{
col1.push_back(i);
}
/* print number of elements by processing the distance between beginning and end
* - NOTE: uses operator- for iterators
*/
cout << "number/distance: " << col1.end() - col1.begin() << endl;
/* print all elements
* - NOTE: uses operator < instead of operator !=
*/
vector<int>::iterator pos;
for (pos = col1.begin(); pos < col1.end(); ++pos)
{
cout << *pos << ' ';
}
cout << endl;
/* print all elements
* - NOTE: uses operator[] instead of operator *
*/
for (int i = 0; i < col1.size(); ++i)
{
cout << col1.begin()[i] << ' ';
}
cout << endl;
/* print every second element
* - NOTE: uses operator +=
*/
for (pos = col1.begin(); pos < col1.end() - 1; pos += 2)
{
cout << *pos << ' ';
}
cout << endl;
}
程序說明:有NOTE標(biāo)識的操作只適用于random access iterator。
output:7.2.6 vector迭代器increment / decrement
一般可遞增或遞減暫時性iterator,但對于vector/string則不行。
eg:
vector<int> col;
//...
if (col.size() > 1){
sort(++col.begin(), col.end() );
}
通常編譯sort()會失敗,若換deque取代vector則可通過編譯,
有時vector也可通過編譯——取決于vector具體實現(xiàn)。
為保證可移植性,應(yīng)使用輔助對象,相關(guān)結(jié)構(gòu)改成:
if (col.size() > 1){
vector<int>::iterator beg = col.begin();
sort(++beg, col.end() );
}
主要原因:vector iterator常被實現(xiàn)為一般指針,C++不允許修改任何 基本類型(含指針)的暫時值,對struct/class則可以。
7.3 迭代器相關(guān)輔助函數(shù)
C++標(biāo)準(zhǔn)庫為迭代器提供三個輔助函數(shù):advance()、distance()、iter_swap()。
7.2.1 advance() 可使迭代器前進(jìn)/后退
#include <iterator>
void advance (InputIterator& pos, Dist n)
使input iterator前進(jìn)/后退 n個元素;
對bidirectional/random access iterator,n可為負(fù)值表后退;
Dist是template類型。通常 是整數(shù),因為會調(diào)用<、++、--等,及和0比較;
不檢查迭代器是否超過end(),
因為迭代器通常不知道其所操作的容器。
對random access iterator,advance()調(diào)用pos+=n,
對其它類型迭代器則調(diào)用++pos或--pos共n次。
若希望程序能方便更換容器和迭代器種類,應(yīng)用advance()而不是operator+=。
但對于不提供random access iterator的容器,性能會變差。
另,advance()無返回值,operator+=有返回值,后者更強大。
eg:
// advance()運用示例
// iter/advance1.cpp
#include <iostream>
#include <list>
#include <algorithm>
using namespace std;
int main()
{
list<int> col1;
// insert elements from 1 to 9
for (int i = 1; i <= 9; ++i)
{
col1.push_back(i);
}
list<int>::iterator pos = col1.begin();
// print actual element
cout << *pos << endl;
// step one element backward
advance (pos , -1);
// print actual element
cout << *pos << endl;
// step three element forward
advance (pos , 3);
// print actual element
cout << *pos << endl;
}
output:7.3.2 distance()處理迭代器間的距離
#include <iterator>
Dist distance (InputIterator pos1, InputIterator pos2)
返回input iterator pos1和pos2的距離;
pos1和pos2須指向同一容器;
若不是random access iterator,則從pos1須能到達(dá)pos2;
返回值Dist類型有迭代器決定:iterator_traits<InputIterator>::difference_type
對于random iterator直接返回pos2-pos1,
對于其它iterator則不斷遞增pos1直到pos2止,然后返回遞增次數(shù)。
eg:
// distance()示例
// iter/distance.cpp
#include <iostream>
#include <list>
#include <algorithm>
using namespace std;
int main()
{
list<int> col1;
// insert elements from -3 to 9
for (int i = -3; i <= 9; ++i)
{
col1.push_back(i);
}
// search elements with value 5
list<int>::iterator pos;
pos = find(col1.begin(), col1.end(), // range
5); // value
if (pos != col1.end() )
{
// process and print difference from the beginning
cout << "difference between beginning and 5: "
<< distance(col1.begin(), pos) << endl;
}
else
{
cout << "5 not found" << endl;
}
}
output:同advance(),若希望方便更換容器類型和迭代器類型,應(yīng)用distance()而不是operator-。
對non-random iterator,當(dāng)pos1在pos2之后時,會導(dǎo)致未定義行為。
7.3.3 iter_swap()可交換兩迭代器所指內(nèi)容
#include <algorithm>
void iter_swap (ForwardIterator1 pos1, ForwardIterator2 pos2)
交換pos1和pos2所指的值;
pos1和pos2的類型不必相同但所指的兩個值必須可相互賦值。
eg:
// iter/swap1.cpp
#include <iostream>
#include <list>
#include "../stl/print.hpp"
using namespace std;
int main()
{
list<int> col1;
// insert elements from 1 to 9
for (int i = 1; i <= 9; ++i)
{
col1.push_back(i);
}
PRINT_ELEMENTS(col1);
// swap first and second value
iter_swap (col1.begin(), ++col1.begin());
PRINT_ELEMENTS(col1);
// swap first and last value
iter_swap(col1.begin(), --col1.end());
PRINT_ELEMENTS(col1);
}
output:7.4 Iterator adapter
此類特殊迭代器使得算法能夠以reverse mode / insert mode進(jìn)行工作,也可和stream搭配工作。
7.4.1 reverse iterator
reverse iterator是一種配接器,重定義遞增、遞減運算,使其行為正好相反。
// iter/reviter1.cpp
#include <iostream>
#include <list>
#include <algorithm>
using namespace std;
void print (int elem)
{
cout << elem << ' ';
}
int main()
{
list<int> col1;
// insert elements from 1 to 9
for (int i = 1; i <= 9; ++i)
{
col1.push_back(i);
}
// print all elements in normal order
for_each (col1.begin(), col1.end(), // range
print); // operation
cout << endl;
// print all elements in reverse order
for_each (col1.rbegin(), col1.rend(), // range
print); // operation
cout << endl;
}
output:-
迭代器和逆向迭代器
可將一般迭代器轉(zhuǎn)換為reverse iterator,但那個迭代器須可雙向移動。且,轉(zhuǎn)換前后迭代器的邏輯位置發(fā)生了變化。
// iter/reviter2.cpp
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main()
{
vector<int> col1;
// insert elements from 1 to 9
for (int i = 1; i <= 9; ++i)
{
col1.push_back(i);
}
// find position of element with value 5
vector<int>::iterator pos;
pos = find (col1.begin(), col1.end(), 5);
// print value to which iterator pos refers
cout << "pos: " << *pos << endl;
// convert iterator to reverse iterator rpos
vector<int>::reverse_iterator rpos(pos);
// print value to which reverse iterator rpos refers
cout << "rpos: " << *rpos << endl;
}
output:
迭代器轉(zhuǎn)換為reverse iterator后邏輯位置變化的原因:區(qū)間半開性,即實際上reverse iterator倒置了半開原則。
rbegin() 即 container::reverse_iterator(end() ),
rend() 即 container::reverse_iterator(begin() )。
但若是一對迭代器定義的區(qū)間,變換操作就很簡單且元素有效。
// iter/reviter3.cpp
#include <iostream>
#include <deque>
#include <algorithm>
using namespace std;
void print(int elem)
{
cout << elem << ' ';
}
int main()
{
deque<int> col1;
// insert elements from 1 to 9
for (int i = 1; i <= 9; ++i)
{
col1.push_back(i);
}
// find position of element with value 3
deque<int>::iterator pos1;
pos1 = find (col1.begin(), col1.end(), 2);
// find positon of element with value 7
deque<int>::iterator pos2;
pos2 = find (col1.begin(), col1.end(), 7);
// print all elements in range [pos1, pos2)
for_each (pos1, pos2, print);
cout << endl;
// convert iterator to reverse iterator
deque<int>::reverse_iterator rpos1(pos1);
deque<int>::reverse_iterator rpos2(pos2);
// print all elements in range [pos1, pos2) in reverse order
for_each (rpos2, rpos1, print);
cout << endl << endl;
for_each (rpos1, rpos2, print);
cout << endl;
}
output:-
base()將reverse iterator轉(zhuǎn)換為 一般迭代器
base()函數(shù)可見STL源碼剖析。
// iter/reviter4.cpp
#include <iostream>
#include <list>
#include <algorithm>
using namespace std;
int main()
{
list<int> col1;
// insert elements from 1 to 9
for (int i = 1; i <= 9; ++i)
{
col1.push_back(i);
}
// find position of element with value 5
list<int>::iterator pos;
pos = find (col1.begin(), col1.end(), 5);
// print value of the element
cout << "pos: " << *pos << endl;
// convert iterator to reverse iterator
list<int>::reverse_iterator rpos(pos);
// print value of the element to whcih the reverse iterator refers
cout << "rpos: " << *rpos << endl;
// convert reverse iterator back to normal iterator
list<int>::iterator rrpos;
rrpos = rpos.base();
// print value of the element to which the normal iterator refers
cout << "rrpos: " << *rrpos << endl;
}
output:7.4.2 insert iterator
也稱為inserter。通過inserter,算法可insert而非overwrite。
inserter隸屬于output iterator,故只提供assignment。
通常算法會賦值給iterator,如copy()算法:
namespace std
{
template<class Inputiterator, class Outputiterator>
Outputiterator copy (Inputiterator from_pos, // beginning of source
Inputiterator from_end, // end of source
OutputIterator to_pos) // beginning of dest
{
while (from_pos != from_end)
{
*to_pos = *from_pos; / copy value
++from_pos; // increment iterator
++to_pos;
}
}
}
note: *to_pos = value;語句,insert iterator將此類賦值操作轉(zhuǎn)化為插入操作。
對于insert iterator的轉(zhuǎn)化過程:
首先operator* 返回iterator當(dāng)前位置(insert iterator中視為一個 no-op,只簡單傳回*this。),
然后operator=賦值(調(diào)用容器的push_back()/push_front()/insert() )。
-
insert iterator種類
t7-8.png
由于push_back()只存在于vector/deque/list/string中,故C++標(biāo)準(zhǔn)庫中只有這些容器支持back_inserter。
// iter/backins.cpp
#include <iostream>
#include <vector>
#include <algorithm>
#include "../stl/print.hpp"
using namespace std;
int main()
{
vector<int> col1;
// create back inserter for col1
// - inconvenient way
back_insert_iterator<vector<int> > iter(col1);
// insert element with the usual iterator interface
*iter = 1;
iter++;
*iter = 2;
iter++;
*iter = 3;
PRINT_ELEMENTS(col1);
// create back inserter and insert elements
// - convenient way
back_inserter(col1) = 44;
back_inserter(col1) = 55;
PRINT_ELEMENTS(col1);
// use back inserter to append all elements again
// - reverse enough memory to avoid realllocation
col1.reserve(2*col1.size() );
copy(col1.begin(), col1.end(), // source
back_inserter(col1) ); // destination
PRINT_ELEMENTS(col1);
}
output:同理push_front()只在deque/list中有實現(xiàn),故front_inserter()只支持deque/list。
// iter/frontins.cpp
#include <iostream>
#include <list>
#include <algorithm>
#include "../stl/print.hpp"
using namespace std;
int main()
{
list<int> col1;
// create front inserter for col1
// - inconvenient way
front_insert_iterator<list<int> > iter(col1);
// insert elements with the usual iterator interface
*iter = 1;
iter++;
*iter = 2;
iter++;
*iter = 3;
PRINT_ELEMENTS(col1);
// create front inserter and insert elements
// - convenient way
front_inserter(col1) = 44;
front_inserter(col1) = 55;
PRINT_ELEMENTS(col1);
// use front inserter to insert all elements again
copy (col1.begin(), col1.end(), front_inserter(col1));
PRINT_ELEMENTS(col1);
}
output:general inserter根據(jù)兩個參數(shù)初始化:容器和待插入位置。
所有容器都提供insert(),故適用于所有容器,
注意“待插入位置”對關(guān)聯(lián)式容器只是提示。
插入操作完成后,general inserter會獲得被插入元素的位置,即
pos = container.insert(pos, value);
++pos;
如此是為了確保該迭代器的位置始終有效。
// iter/inserter.cpp
#include <iostream>
#include <set>
#include <list>
#include "../stl/print.hpp"
using namespace std;
int main()
{
set<int> col1;
// create insert iterator for col1;
// - inconvenient way
insert_iterator<set<int> > iter(col1, col1.begin());
// insert elements with the usual iterator interface
*iter = 1;
iter++;
*iter = 2;
iter++;
*iter = 3;
PRINT_ELEMENTS(col1, "set: ");
// create inserter and insert elements
// - convenient way
inserter(col1, col1.end()) = 44;
inserter(col1, col1.end()) = 55;
PRINT_ELEMENTS(col1, "set: ");
// use inesrter to insert all elements into a list
list<int> coll2;
copy(col1.begin(), col1.end(), inserter(coll2, coll2.begin()));
PRINT_ELEMENTS(coll2, "list: ");
// use inserter to reinsert all elements into the list before the second element
copy (col1.begin(), col1.end(), inserter(coll2, ++coll2.begin()));
PRINT_ELEMENTS(coll2, "list: ");
}
output:7.4.3 stream iterator
一個istream iterator可從input stream中讀,
一個ostream iterator可對output stream寫。
-
ostream iterator
ostream iterator與insert iterator類似,區(qū)別在于ostream迭代器將賦值操作轉(zhuǎn)化為operator<<。
t7-9.png
// iter/ostriter.cpp
#include <iostream>
#include <vector>
#include <algorithm>
#include <iterator>
using namespace std;
int main()
{
// create ostream iterator for stream cout
// - value are separated by a newline charcter
ostream_iterator<int> intWriter(cout, "\n");
// write elements with the usual iterator interface
*intWriter = 42;
intWriter++;
*intWriter = 77;
intWriter++;
*intWriter = -5;
// create collection with elements from 1 to 9
vector<int> col1;
for (int i = 1; i <= 9; ++i)
{
col1.push_back(i);
}
// write all elements without any delimiter
copy(col1.begin(), col1.end(), ostream_iterator<int>(cout) );
cout << endl;
// write all elements with "<" as delimiter
copy(col1.begin(), col1.end(), ostream_iterator<int>(cout, "<") );
cout << endl;
}
output:
note:分隔符delimiter類型是const char*,若傳入一個string對象,須調(diào)用c_str()成員函數(shù)以獲得正確類型。
- istream iterator
產(chǎn)生istream iterator須提供一個input stream作為參數(shù),iterator從中讀數(shù)據(jù)。
然而,讀操作可能失敗(文件尾部或讀錯誤等原因),算法也需直到區(qū)間是否到終點。
故可用一個end-of-stream迭代器,該迭代器有istream的default ctor生成,
只要任何一個讀取失敗,則所有istream iterator會變成end-of-stream iterator。
故每次讀操作后,須將istream iterator和end-of-istream iterator比較。
兩個istream iterator相等條件:
二者都是end-of-stream iterator或 二者都可進(jìn)行讀操作且指向相同stream。
// iter/istriter.cpp
#include <iostream>
#include <iterator>
using namespace std;
int main()
{
// create istream iterator that reads integers from cin
istream_iterator<int> intReader(cin);
// create end-of-stream iterator
istream_iterator<int> intReaderEOF;
/* while able to read tokens with istream iterator
* write them twice
*/
while (intReader != intReaderEOF)
{
cout << "once: " << *intReader << endl;
cout << "once again: " << *intReader << endl;
++intReader;
}
}
output:stream iterator的另個例子:
// iter/advance2.cpp
#include <iostream>
#include <string>
#include <algorithm>
#include <iterator>
using namespace std;
int main()
{
istream_iterator<string> cinPos(cin);
ostream_iterator<string> coutPos(cout, " ");
/* while input is not at the end of the file
* - write every third string
*/
while (cinPos != istream_iterator<string>() )
{
// ignore the following two string
advance(cinPos, 2);
// read and write the third string
if (cinPos != istream_iterator<string>())
{
*coutPos++ = *cinPos++;
}
}
cout << endl;
}
output:7.5 iterator traits
根據(jù)不同iterator對operation重載,具體實現(xiàn)通過iterator tag和trait(由<iterator>提供)實現(xiàn)重載。
C++標(biāo)準(zhǔn)庫為每種iterator提供一個iterator tag作為其label:
namespace std
{
struct output_iterator_tag{};
struct input_iterator_tag{};
struct forward_iterator_tag : public input_iterator_tag{};
struct bidirectional_iterator_tag : public forward_iterator_tag{};
struct random_access_iterator_tag : public bidirectional_iterator_tag{};
}
若進(jìn)行泛型編程,需要了解iterator所指元素類型,C++標(biāo)準(zhǔn)庫提供一種template結(jié)構(gòu)定義 iterator trait:
namespace std
{
template<class T>
struct iterator_traits
{
typedef typename T::value_type value_type;
typedef typename T::difference_type difference_type;
typedef typename T::iterator_category iterator_category;
typedef typename T::pointer pointer;
typedef typename T::reference reference;
};
}
7.5.1 為iterator編寫泛型函數(shù)
- 運用iterator type
eg1:以元素類型為類型的零時變量。
typename std::iterator_traits<T>::value_type tmp;
eg2:元素環(huán)形移動。
template<class Forwarditerator>
void shift_left (Forwarditerator beg, Forwarditerator end)
{
// temporary variable for first element
typedef typename
std::iterator_traits<Forwarditerator>::value_type value_type;
if (beg != end)
{
// save value of first element
value_type tmp(*beg);
// shift following values
//...
}
}
-
運用iterator category
對不同iterator類型進(jìn)行不同的實現(xiàn),需兩步:
1. 把template函數(shù)將iterator類型做為參數(shù),調(diào)用另個函數(shù)。eg:
template <class iterator>
inline void foo(iterator beg, iterator end)
{
foo(beg,end,
std::iterator_traits<iterator>::iterator_category() );
}
2. 針對不同iterator實現(xiàn)不同的 步驟一中所調(diào)用的函數(shù)。
只有“非派生自其它iterator類型”的iterator類型,才要提供特殊版本。eg:
// foo() for bidirectional iterator
template <class BiIterator>
void foo(BiIterator beg, BiIterator end,
std::bidirectioanl_iterator_tag){ // ...}
// foo() for random access iterator
template <class RaIterator>
void foo (RaIterator beg, RaIterator end,
std::random_access_iterator_tag){ //...}
- distance() 實現(xiàn)
// general distance()
template <class iterator>
typename std::iterator_traits<iterator>::difference_type
distance (iterator pos1, iterator pos2)
{
return distance(pos1, pos2,
std::iterator_traits<iterator>::iterator_category() );
}
// distance() for random access iterator
template <class Raiterator>
typename std::iterator_traits<Raiterator>::difference_type
distance (Raiterator pos1, Raiterator pos2,
std::random_access_iterator_tag)
{
return pos2 - pos1;
}
// distance() for input,forward, and bidirectional iterator
template <class Initerator>
typename std::iterator_traits<Initerator>::difference_type
distance(Initerator pos1, Initerator pos2,
std::input_iterator_tag)
{
typename std::iterator_traits<Initerator>::difference_type d;
for ( d = 0; pos1 != pos2; ++pos1, ++d){;}
return d;
}
7.5.2 user-defined iterator
自定義iterator需要為iterator提供traits,有兩種可行方法:
- 1. 提供一個特殊版本的iterator_traits結(jié)構(gòu)。
- 2. 提供必要的五種類型定義,如iterator_traits結(jié)構(gòu)所述。
C++標(biāo)準(zhǔn)庫提供了一個特殊的基礎(chǔ)類別iterator<>,用于定義自定義iterator。
class Myiterator
: public std::iterator<std::bidirectional_iterator_tag,
type, std::ptrdiff_t, type*, type&>{ //...};
第一個參數(shù)定義iterator類型,第二個參數(shù)定義元素類型,第三個參數(shù)定義距離類型,
第四個參數(shù)定義pointer類型,第五個參數(shù)定義reference類型。
后三個參數(shù)有默認(rèn)值ptrdiff_t,type*,type&。
eg:關(guān)聯(lián)式容器的insert iterator
// 關(guān)聯(lián)式容器的 insert iterator
// iter/assoiter.hpp
#include <iterator>
/* template class for insert iterator for associative containers */
template <class Container>
class asso_insert_iterator :
public std::iterator<std::output_iterator_tag, void, void, void, void>
{
protected:
Container& container; //container in which element are inserted
public:
// constructor
explicit asso_insert_iterator (Container& c) : container(c){}
// assignment operator
// - inserts a value into the container
asso_insert_iterator<Container>&
operator= (const typename Container::value_type& value)
{
container.insert(value);
return *this;
}
// dereference is a no-op that returns the iterator itself
asso_insert_iterator<Container>& operator*()
{
return *this;
}
// increment operation is a no-op that returns the iterator itself
asso_insert_iterator<Container>& operator++()
{
return *this;
}
asso_insert_iterator<Container>& operator++(int)
{
return *this;
}
};
/* convenience function to create the inserter */
template <class Container>
inline asso_insert_iterator<Container> asso_inserter(Container& c)
{
return asso_insert_iterator<Container>(c);
}
使用:
// iter/assoiter.cpp
#include <iostream>
#include <set>
#include <algorithm>
using namespace std;
#include "../stl/print.hpp"
#include "assoiter.hpp"
int main()
{
set<int> col1;
// create inserter for col1
// -inconvenient way
asso_insert_iterator<set<int> > iter(col1);
// insert elements with the usual iterator interface
*iter = 1;
iter++;
*iter = 2;
iter++;
*iter = 3;
PRINT_ELEMENTS(col1);
// create inserter for col1 and insert elements
// - convenient way
asso_inserter(col1) = 44;
asso_inserter(col1) = 55;
PRINT_ELEMENTS(col1);
// use inserter with an algorithm
int vals[] = {33, 67, -4, 13, 5, 2};
copy(vals, vals + sizeof(vals)/sizeof(vals[0]), asso_inserter(col1));
PRINT_ELEMENTS(col1);
}
output: