迭代器
標(biāo)準(zhǔn)庫(kù)容器類型上所有迭代器都允許我們?cè)L問(wèn)容器中的元素,下面的表中列出了容器迭代器支持的所有操作揍异,其中有一個(gè)例外:forward_list迭代器不支持遞減運(yùn)算符。
迭代器操作 | 說(shuō)明 |
---|---|
*iter | 返回迭代器所指元素的引用 |
iter->member | 解引用iter并獲取該元素的名為member的成員爆班,等價(jià)于(*iter).member |
++iter | 令iter指向容器中下一個(gè)元素 |
--iter | 令iter指向容器中上一個(gè)元素 |
iter1 == iter2 | 判斷兩個(gè)迭代器是否指向的是同一個(gè)元素或者是同一個(gè)容器的尾后迭代器 |
iter1 != iter2 | 同上 |
容器中的begin和end迭代器指示了容器中的元素范圍衷掷,其中end迭代器不指向最后一個(gè)元素,而是指向尾元素之后的位置柿菩,這種元素范圍被稱為左閉合區(qū)間:[begin,end)戚嗅,假設(shè)begin和end構(gòu)成了一個(gè)合法的迭代器范圍,則:
- 如果begin與end相等,則范圍為空懦胞。
- 如果begin與end不等替久,則范圍至少包含一個(gè)元素,且begin指向該范圍中的第一個(gè)元素躏尉。
- 我們可以對(duì)begin遞增若干次使得begin==end蚯根。
begin和end有多個(gè)版本,其中帶r的版本返回反向迭代器胀糜,c開(kāi)頭的版本返回const迭代器颅拦。
使用迭代器遍歷數(shù)組:
int main()
{
int values1[] = { 1,2,3,4,5 };
for (auto it = begin(values1); it != end(values1); it++) {
cout << *it << endl;
}
system("pause");
}
插入迭代器
插入迭代器是一種迭代器適配器,它接受一個(gè)容器教藻,生成一個(gè)迭代器距帅,能實(shí)現(xiàn)向給定容器添加元素,當(dāng)我們給一個(gè)插入迭代器賦值時(shí)括堤,迭代器調(diào)用容器操作來(lái)向給定容器的指定位置插入一個(gè)元素碌秸,插入完成后迭代器保持不變。*it,++it,it++不會(huì)對(duì)插入迭代器做任何事情痊臭,每個(gè)操作都返回it哮肚。
插入迭代器有三種類型:
- back_inserter,創(chuàng)建一個(gè)使用push_back的迭代器广匙。
- front_inserter允趟,創(chuàng)建一個(gè)使用push_front的迭代器。
- inserter創(chuàng)建一個(gè)使用insert的迭代器鸦致,此函數(shù)接受第二個(gè)參數(shù)潮剪,這個(gè)參數(shù)必須是一個(gè)指向給定容器的迭代器,元素將被插入到給定迭代器所表示的元素之前分唾。
int main()
{
vector<int> values{ 1,2,3,4 };
list<int> list1, list2,list3;
copy(values.cbegin(), values.cend(), back_inserter(list1));
copy(values.cbegin(), values.cend(), front_inserter(list2));
copy(values.cbegin(), values.cend(), inserter(list3,list3.begin()));
for (const auto& e : list1) {
cout << e << endl;//1 2 3 4
}
for (const auto& e : list2) {
cout << e << endl;//4 3 2 1
}
for (const auto& e : list3) {
cout << e << endl;//1 2 3 4
}
system("pause");
}
iostream迭代器
雖然iostream不是容器抗碰,但標(biāo)準(zhǔn)庫(kù)定義了可以用于這些IO類型對(duì)象的迭代器,istream_iterator讀取輸入流绽乔,ostream_iterator向一個(gè)輸出流寫(xiě)數(shù)據(jù)弧蝇,這些迭代器將它們對(duì)應(yīng)的流當(dāng)作一個(gè)特定類型的元素序列來(lái)處理,通過(guò)使用流迭代器折砸,我們可以用泛型算法從流對(duì)象讀取數(shù)據(jù)或?qū)懭霐?shù)據(jù)看疗。
創(chuàng)建一個(gè)流迭代器時(shí),必須指定迭代器將要讀寫(xiě)的對(duì)象類型睦授,還可以將流迭代器綁定到一個(gè)流两芳,如果不綁定它將是一個(gè)可以當(dāng)作尾后值使用的流迭代器。一旦關(guān)聯(lián)的流遇到文件尾或遇到IO錯(cuò)誤去枷,迭代器的值就與尾后迭代器相等怖辆。
int main()
{
vector<int> values;
istream_iterator<int> inIter(cin);
istream_iterator<int> eof;
while (inIter != eof) {
//先調(diào)用后置遞增運(yùn)算符是复,然后對(duì)迭代器的舊值進(jìn)行解引用
values.push_back(*inIter++);
}
for (const auto& e : values) {
cout<<e<<endl;
}
system("pause");
}
我們可以將上面的代碼重寫(xiě)為如下形式,values構(gòu)造時(shí)從cin讀取數(shù)據(jù)竖螃,直到遇到文件尾或者不是int的數(shù)據(jù)為止淑廊。
int main()
{
istream_iterator<int> inIter(cin), eof;
vector<int> values(inIter, eof);
for (const auto& e : values) {
cout<<e<<endl;
}
system("pause");
}
由于算法使用迭代器操作來(lái)處理數(shù)據(jù),而流迭代器又至少支持某些迭代器操作斑鼻,因此我們可以使用某些算法來(lái)操作流迭代器蒋纬,下面的代碼計(jì)算從輸入流讀取的值的和。
int main()
{
istream_iterator<int> inIter(cin), eof;
cout << accumulate(inIter, eof, 0) << endl;
system("pause");
}
當(dāng)我們將一個(gè)istream_iterator綁定到一個(gè)流時(shí)坚弱,標(biāo)準(zhǔn)庫(kù)并不保證迭代器立即從流讀取數(shù)據(jù)蜀备,具體實(shí)現(xiàn)可以推遲從流中讀取數(shù)據(jù),標(biāo)準(zhǔn)庫(kù)保證的是在我們解引用迭代器之前荒叶,從流中讀取的操作已經(jīng)完成了碾阁。
int main()
{
vector<int> values;
istream_iterator<int> inIter(cin);
//輸入一個(gè)值后才會(huì)顯示ready now
cout << "ready now" << endl;
values.push_back(*inIter);
system("pause");
}
當(dāng)創(chuàng)建一個(gè)ostream_iterator時(shí),我們可以提供(可選的)第二個(gè)參數(shù)些楣,它是一個(gè)C風(fēng)格字符串脂凶,在輸出每個(gè)元素后都會(huì)打印此字符串。
ostream_iterator必須綁定到一個(gè)流愁茁,*out蚕钦,++out,out++這些運(yùn)算符雖然存在但不對(duì)out做任何事情鹅很,每個(gè)運(yùn)算符都返回out嘶居。
對(duì)ostream_iterator使用賦值運(yùn)算符(=)時(shí),會(huì)用<<運(yùn)算符將等號(hào)右邊的值寫(xiě)入到所綁定的流中促煮,該值的類型必須與流可寫(xiě)的類型兼容邮屁。
int main()
{
ostream_iterator<int> outIter(cout," ");
vector<double> values{1.0,2.0,3.0};
for (const auto& e : values) {
outIter = e;//1 2 3
}
system("pause");
}
還可以通過(guò)copy打印vector中的元素,這比上面的代碼更簡(jiǎn)單菠齿。
int main()
{
ostream_iterator<int> outIter(cout," ");
vector<int> values{1,2,3};
copy(values.cbegin(), values.cend(), outIter);//1 2 3
system("pause");
}
反向迭代器
反向迭代器就是在容器中從尾元素向首元素反向移動(dòng)的迭代器佑吝,對(duì)于反向迭代器,遞增或遞減的操作含義會(huì)顛倒過(guò)來(lái)绳匀。通過(guò)成員函數(shù)base可以獲得反向迭代器對(duì)應(yīng)的普通迭代器芋忿。
int main()
{
vector<int> values1 = { 10,2,8,0,9,1 };
//按逆序排序
sort(values1.rbegin(),values1.rend());
ostream_iterator<int> outIter(cout," ");
copy(values1.cbegin(), values1.cend(), outIter);//10 9 8 2 1 0
//反向輸出
copy(values1.crbegin(), values1.crend(), outIter);//0 1 2 8 9 10
//通過(guò)base()正向輸出
copy(values1.crend().base(), values1.crbegin().base(), outIter);//10 9 8 2 1 0
system("pause");
}
移動(dòng)迭代器
移動(dòng)迭代器通過(guò)改變給定迭代器的解引用運(yùn)算符的行為來(lái)適配此迭代器,普通迭代器的解引用運(yùn)算符返回一個(gè)指向元素的左值疾棵,而移動(dòng)迭代器解引用運(yùn)算符生成一個(gè)右值引用盗飒。我們通過(guò)調(diào)用make_move_iterator將普通迭代器轉(zhuǎn)換為一個(gè)移動(dòng)迭代器。
由于移動(dòng)一個(gè)對(duì)象可能銷毀原對(duì)象陋桂,因此只有在確信算法或自定義的函數(shù)使用完元素后不再訪問(wèn)它時(shí),才能將移動(dòng)迭代器傳遞過(guò)去蝶溶。
在庫(kù)代碼中使用移動(dòng)操作(std::move,make_move_iterator)能提升性能嗜历,但是如果在用戶代碼中隨意使用移動(dòng)操作宣渗,很可能會(huì)導(dǎo)致莫名其妙,難以查找的錯(cuò)誤梨州。