C++
迭代器 基礎(chǔ)介紹 迭代器提供對一個(gè)容器中的對象的訪問方法,,并且定義了容器中對象的范圍,。迭代器就如同一個(gè)指針。事實(shí)上,,C++的指針也是一種迭代器,。但是,迭代器不僅僅是指針,,因此你不能認(rèn)為他們一定具有地址值,。例如,一個(gè)數(shù)組索引,,也可以認(rèn)為是一種迭代器,。 除了使用下標(biāo)來訪問 vector 對象的元素外,標(biāo)準(zhǔn)庫還提供了另一種訪問元素的方法:使用迭代器(iterator),。迭代器是一種檢查容器內(nèi)元素并遍歷元素的數(shù)據(jù)類型,。 標(biāo)準(zhǔn)庫為每一種標(biāo)準(zhǔn)容器(包括 vector)定義了一種迭代器類型。迭代器類型提供了比下標(biāo)操作更通用化的方法:所有的標(biāo)準(zhǔn)庫容器都定義了相應(yīng)的迭代器類型,,而只有少數(shù)的容器支持下標(biāo)操作,。因?yàn)榈鲗λ械娜萜鞫歼m用,現(xiàn)代 C++ 程序更傾向于使用迭代器而不是下標(biāo)操作訪問容器元素,,即使對支持下標(biāo)操作的
vector 類型也是這樣,。 容器的 iterator 類型 每種容器類型都定義了自己的迭代器類型,如 vector: vector<int>::iterator iter; 這符語句定義了一個(gè)名為 iter 的變量,它的數(shù)據(jù)類型是 vector<int> 定義的 iterator 類型,。每個(gè)標(biāo)準(zhǔn)庫容器類型都定義了一個(gè)名為 iterator 的成員,,這里的 iterator 與迭代器實(shí)際類型的含義相同。 術(shù)語:迭代器和迭代器類型 程序員首次遇到有關(guān)迭代器的術(shù)語時(shí)可能會(huì)困惑不解,,原因之一是由于同一個(gè)術(shù)語 iterator 往往表示兩個(gè)不同的事物,。一般意義上指的是迭代器的概念;而具體而言時(shí)指的則是由容器定義的具體的 iterator 類型,,如 vector<int>,。 重點(diǎn)要理解的是,有許多用作迭代器的類型,,這些類型在概念上是相關(guān)的,。若一種類型支持一組確定的操作(這些操作可用來遍歷容器內(nèi)的元素,并訪問這些元素的值),,我們就稱這種類型為迭代器,。 各容器類都定義了自己的 iterator 類型,用于訪問容器內(nèi)的元素,。換句話說,,每個(gè)容器都定義了一個(gè)名為
iterator 的類型,而這種類型支持(概念上的)迭代器的各種操作,。 begin
和 end 操作 每種容器都定義了一對命名為 begin 和 end 的函數(shù),,用于返回迭代器。如果容器中有元素的話,,由 begin 返回的迭代器指向第一個(gè)元素: vector<int>::iterator iter =
ivec.begin(); 上述語句把 iter 初始化為由名為 vector 操作返回的值,。假設(shè) vector 不空,,初始化后,,iter 即指該元素為 ivec[0]。 由 end
操作返回的迭代器指向 vector 的“末端元素的下一個(gè)”,?!俺瞿┒说鳌保?span lang="EN-US">off-the-end iterator)。表明它指向了一個(gè)不存在的元素,。如果
vector 為空,,begin 返回的迭代器與 end 返回的迭代器相同。
由 end
操作返回的迭代器并不指向 vector 中任何實(shí)際的元素,,相反,,它只是起一個(gè)哨兵(sentinel)的作用,表示我們已處理完 vector 中所有元素,。 vector
迭代器的自增和解引用運(yùn)算 迭代器類型定義了一些操作來獲取迭代器所指向的元素,,并允許程序員將迭代器從一個(gè)元素移動(dòng)到另一個(gè)元素。 迭代器類型可使用解引用操作符(dereference operator)(*)來訪問迭代器所指向的元素: *iter = 0; 解引用操作符返回迭代器當(dāng)前所指向的元素。假設(shè) iter 指向 vector 對象
ivec 的第一元素,,那么 *iter 和 ivec[0] 就是指向同一個(gè)元素,。上面這個(gè)語句的效果就是把這個(gè)元素的值賦為
0。 迭代器使用自增操作符向前移動(dòng)迭代器指向容器中下一個(gè)元素,。從邏輯上說,,迭代器的自增操作和 int 型對象的自增操作類似。對 int 對象來說,,操作結(jié)果就是把 int 型值“加 1”,,而對迭代器對象則是把容器中的迭代器“向前移動(dòng)一個(gè)位置”。因此,,如果 iter 指向第一個(gè)元素,,則 ++iter 指向第二個(gè)元素。 由于
end 操作返回的迭代器不指向任何元素,,因此不能對它進(jìn)行解引用或自增操作,。 迭代器的其他操作 另一對可執(zhí)行于迭代器的操作就是比較:用 == 或 != 操作符來比較兩個(gè)迭代器,如果兩個(gè)迭代器對象指向同一個(gè)元素,,則它們相等,,否則就不相等。 迭代器應(yīng)用的程序示例 假設(shè)已聲明了一個(gè) vector<int> 型的 ivec 變量,,要把它所有元素值重置為 0,,可以用下標(biāo)操作來完成: // reset all the elements in ivec to 0 for (vector<int>::size_type ix = 0; ix
!= ivec.size(); ++ix) ivec[ix] = 0; 上述程序用
for 循環(huán)遍歷 ivec 的元素,for 循環(huán)定義了一個(gè)索引 ix ,,每循環(huán)迭代一次 ix 就自增
1,。for 循環(huán)體將 ivec 的每個(gè)元素賦值為 0。 更典型的做法是用迭代器來編寫循環(huán): // equivalent loop using iterators to reset
all the elements in ivec to 0 for
(vector<int>::iterator iter = ivec.begin();iter != ivec.end(); ++iter) *iter = 0; // set element to which iter refers to 0
for 循環(huán)首先定義了 iter,,并將它初始化為指向 ivec 的第一個(gè)元素,。for 循環(huán)的條件測試 iter 是否與
end 操作返回的迭代器不等。每次迭代 iter 都自增 1,,這個(gè) for 循環(huán)的效果是從 ivec 第一個(gè)元素開始,,順序處理 vector 中的每一元素。最后,, iter 將指向 ivec 中的最后一個(gè)元素,,處理完最后一個(gè)元素后,iter 再增加 1,,就會(huì)與 end 操作的返回值相等,,在這種情況下,循環(huán)終止,。 for 循環(huán)體內(nèi)的語句用解引用操作符來訪問當(dāng)前元素的值,。和下標(biāo)操作符一樣,解引用操作符的返回值是一個(gè)左值,因此可以對它進(jìn)行賦值來改變它的值,。上述循環(huán)的效果就是把 ivec 中所有元素都賦值為 0,。 通過上述對代碼的詳細(xì)分析,可以看出這段程序與用下標(biāo)操作符的版本達(dá)到相同的操作效果:從 vector 的第一個(gè)元素開始,,把 vector 中每個(gè)元素都置為 0,。 本節(jié)給出的例子程序,如果 vector 為空,,程序是安全的,。如果 ivec 為空,則 begin 返回的迭代器不指向任何元素——由于沒有元素,,所以它不能指向任何元素,。在這種情況下,從 begin 操作返回的迭代器與從 end 操作返回的迭代器的值相同,,因此 for 語句中的測試條件立即失敗,。 const_iterator 前面的程序用 vector::iterator 改變 vector 中的元素值。每種容器類型還定義了一種名為 const_iterator 的類型,,該類型只能用于讀取容器內(nèi)元素,,但不能改變其值。 當(dāng)我們對普通 iterator 類型解引用時(shí),,得到對某個(gè)元素的非 const(2.5 節(jié)),。而如果我們對 const_iterator 類型解引用時(shí),則可以得到一個(gè)指向 const 對象的引用(2.4 節(jié)),,如同任何常量一樣,,該對象不能進(jìn)行重寫。 例如,,如果
text 是 vector<string> 類型,,程序員想要遍歷它,輸出每個(gè)元素,,可以這樣編寫程序: // use const_iterator because we won't
change the elements for (vector<string>::const_iterator
iter = text.begin();iter != text.end(); ++iter) cout << *iter << endl; // print
each element in text 除了是從迭代器讀取元素值而不是對它進(jìn)行賦值之外,,這個(gè)循環(huán)與前一個(gè)相似。由于這里只需要借助迭代器進(jìn)行讀,,不需要寫,這里把 iter 定義為 const_iterator 類型,。當(dāng)對 const_iterator 類型解引用時(shí),,返回的是一個(gè) const 值。不允許用 const_iterator: 進(jìn)行賦值 for (vector<string>::const_iterator
iter = text.begin();iter != text.end(); ++ iter) *iter = " "; // error: *iter is const
使用
const_iterator 類型時(shí),,我們可以得到一個(gè)迭代器,,它自身的值可以改變,但不能用來改變其所指向的元素的值??梢詫Φ鬟M(jìn)行自增以及使用解引用操作符來讀取值,,但不能對該元素賦值。 不要把
const_iterator 對象與 const 的
iterator 對象混淆起來,。聲明一個(gè) const 迭代器時(shí),,必須初始化迭代器。一旦被初始化后,,就不能改變它的值: vector<int> nums(10); // nums is nonconst const vector<int>::iterator cit =
nums.begin(); *cit = 1;
// ok: cit can change its underlying element ++cit;
// error: can't change the value of cit const_iterator
對象可以用于 const vector 或非 const vector,,因?yàn)椴荒芨膶懺刂怠?span lang="EN-US">const 迭代器這種類型幾乎沒什么用處:一旦它被初始化后,只能用它來改寫其指向的元素,,但不能使它指向任何其他元素,。 const vector<int> nines(10, 9); // cannot change elements in nines // error: cit2 could change the element it
refers to and nines is const const vector<int>::iterator cit2 =
nines.begin(); // ok: it can't change an element value, so
it can be used with a const vector<int> vector<int>::const_iterator it =
nines.begin(); *it = 10; // error: *it is const ++it;
// ok: it isn't const so we can change its value // an iterator that cannot write elements vector<int>::const_iterator // an iterator whose value cannot change const
vector<int>::iterator 迭代器的算術(shù)操作 除了一次移動(dòng)迭代器的一個(gè)元素的增量操作符外,vector 迭代器(其他標(biāo)準(zhǔn)庫容器迭代器很少)也支持其他的算術(shù)操作,。這些操作稱為迭代器算術(shù)操作(iterator arithmetic),,包括: iter + n iter - n 可以對迭代器對象加上或減去一個(gè)整形值。這樣做將產(chǎn)生一個(gè)新的迭代器,,其位置在 iter 所指元素之前(加)或之后(減) n 個(gè)元素的位置,。加或減之后的結(jié)果必須指向 iter 所指 vector 中的某個(gè)元素,或者是 vector 末端的后一個(gè)元素,。加上或減去的值的類型應(yīng)該是 vector 的 size_type 或 difference_type 類型(參考下面的解釋),。 iter1 -
iter2 該表達(dá)式用來計(jì)算兩個(gè)迭代器對象的距離,該距離是名為 difference_type 的 signed 類型 size_type 的值,,這里的 difference_type 是 signed 類型,,因?yàn)闇p法運(yùn)算可能產(chǎn)生負(fù)數(shù)的結(jié)果。該類型可以保證足夠大以存儲(chǔ)任何兩個(gè)迭代器對象間的距離,。iter1 與 iter2 兩者必須都指向同一 vector 中的元素,,或者指向 vector 末端之后的下一個(gè)元素。 可以用迭代器算術(shù)操作來移動(dòng)迭代器直接指向某個(gè)元素,,例如,,下面語句直接定位于 vector 中間元素: vector<int>::iterator mid = vi.begin() +
vi.size() / 2; 上述代碼用來初始化 mid 使其指向 vi 中最靠近正中間的元素。這種直接計(jì)算迭代器的方法,,與用迭代器逐個(gè)元素自增操作到達(dá)中間元素的方法是等價(jià)的,,但前者的效率要高得多。 任何改變
vector 長度的操作都會(huì)使已存在的迭代器失效,。例如,,在調(diào)用 push_back 之后,就不能再信賴指向 vector 的迭代器的值了,。 |
|