熟悉C++98/03的對于for循環(huán)就再了解不過了,如果我們要遍歷一個數(shù)組,那么在C++98/03中的實現(xiàn)方式:
int?arr[10]?=?{?1,?2,?3,?4,?5,?6,?7,?8,?9,?10?};?? for?(int?i?=?0;?i?<?10;?i++)?? ????cout?<<?arr[i];
而遍歷容器類的For如下:
std::vectorvec?{1,2,3,4,5,6,7,8,9,10};?? for?(std::vector::iterator?itr?=?vec.begin();?itr?!=?vec.end();?itr++)?? ????cout?<<?*itr;
不管上面哪一種方法,都必須明確的確定for循環(huán)開頭以及結(jié)尾條件,而熟悉C#或者python的人都知道在C#和python中存在一種for的使用方法不需要明確給出容器的開始和結(jié)束條件,就可以遍歷整個容器,幸運的是C++11中引入了這種方法也就是基于范圍的for循環(huán),用基于范圍的for循環(huán)改寫上面兩個例子:
int?arr[10]?=?{?1,?2,?3,?4,?5,?6,?7,?8,?9,?10?};?? for?(auto?n?:?arr)?? ????cout?<<?n;?? ???? std::vectorvec?{1,2,3,4,5,6,7,8,9,10};?? for?(auto?n?:vec)?? ????std::cout?<<?n;
可以看到改寫后的使用方法簡單了很多,代碼的可讀性提升了一個檔次,但是需要注意的在上述對容器的遍歷是只讀的,也就是說遍歷的值是不可修改的,如果需要修改其中元素,可以聲明為auto &:
#include#includeusing?namespace?std; int?main() { std::vectorvec{?1,?2,?3,?4,?5,?6,?7,?8,?9,?10?}; cout?<<?"修改前"?<<?endl; for?(auto?&n?:?vec) std::cout?<<?n++; cout?<<?endl; cout?<<?"修改后"?<<?endl; for?(auto?j?:?vec) std::cout?<<?j; cout?<<?endl; system("pause"); return?0; }
使用時需要注意的地方
1.注意auto自動推導的類型
雖然基于范圍的for循環(huán)使用起來非常的方便,我們不用再去關(guān)注for的開始條件和結(jié)束條件等問題了,但是還是有一些細節(jié)問題在使用的時候需要注意,來看下對于容器map的遍歷:
std::mapmap?=?{?{?"a",?1?},?{?"b",?2?},?{?"c",?3?}?};?? for?(auto?&val?:?map)?? ????cout?<<?val.first?<<?"->"?<<?val.second?<<?endl;
為什么是使用val.first,val.second而不是直接輸出value呢?在遍歷容器的時候,auto自動推導的類型是容器的value_type類型,而不是迭代器,而map中的value_type是std::pair,也就是說val的類型是std::pair類型的,因此需要使用val.first,val.second來訪問數(shù)據(jù)。
2.注意容器本身的約束
使用基于范圍的for循環(huán)還要注意一些容器類本身的約束,比如set的容器內(nèi)的元素本身有容器的特性就決定了其元素是只讀的,哪怕的使用了引用類型來遍歷set元素,也是不能修改器元素的,看下面例子:
setss?=?{?1,?2,?3,?4,?5,?6?};?? for?(auto&?n?:?ss)?? ????cout?<<?n++?<<?endl;
上述代碼定義了一個set,使用引用類型遍歷set中的元素,然后對元素的值進行修改,該段代碼編譯失?。篹rror C3892: 'n' : you cannot assign to a variable that is const。同樣對于map中的first元素也是不能進行修改的。
3.當冒號后不是容器而是一個函數(shù)
再來看看假如我們給基于范圍的for循環(huán)的:冒號后面的表達式不是一個容器而是一個函數(shù),看看函數(shù)會被調(diào)用多少次?
#include#includeusing?namespace?std; setss?=?{?1,?2,?3,?4,?5,?6?}; const?setgetSet() { cout?<<?"GetSet"?<<?endl; return?ss; } int?main() { for?(auto?n?:?getSet()) cout?<<?n?<<?endl; system("pause"); return?0; }
可以看出,如果冒號后面的表達式是一個函數(shù)調(diào)用時,函數(shù)僅會被調(diào)用一次。
4.不要在for循環(huán)中修改容器
#include#includeusing?namespace?std; vectorvec?=?{?1,?2,?3,?4,?5,?6?}; int?main() { for?(auto?n?:?vec) { cout?<<?n?<<?endl; vec.push_back(7); } system("pause"); return?0; }
上述代碼在遍歷vector時,在容器內(nèi)插入一個元素7,運行上述代碼程序崩潰了。
究其原因還是由于在遍歷容器的時候,在容器中插入一個元素導致迭代器失效了,因此,基于范圍的for循環(huán)和普通的for循環(huán)一樣,在遍歷的過程中如果修改容器,會造成迭代器失效,(有關(guān)迭代器失效的問題請參閱C++ primer這本書,寫的很詳細)也就是說基于范圍的for循環(huán)的內(nèi)部實現(xiàn)機制還是依賴于迭代器的相關(guān)實現(xiàn)。