C++11新特性——for遍歷


熟悉C++98/03的對於for循環就再了解不過了,如果我們要遍歷一個數組,那么在C++98/03中的實現方式:


   
   
  
  
          
  1. int arr[ 10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
  2. for ( int i = 0; i < 10; i++)
  3. cout << arr[i];

而遍歷容器類的For如下:

   
   
  
  
          
  1. std:: vector< int> vec { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
  2. for ( std:: vector< int>::iterator itr = vec.begin(); itr != vec.end(); itr++)
  3. cout << *itr;

不管上面哪一種方法,都必須明確的確定for循環開頭以及結尾條件,而熟悉C#或者python的人都知道在C#和python中存在一種for的使用方法不需要明確給出容器的開始和結束條件,就可以遍歷整個容器,幸運的是C++11中引入了這種方法也就是基於范圍的for循環,用基於范圍的for循環改寫上面兩個例子:

   
   
  
  
          
  1. int arr[ 10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
  2. for ( auto n : arr)
  3. cout << n;
  4. std:: vector< int> vec { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
  5. for ( auto n :vec)
  6. std:: cout << n;

可以看到改寫后的使用方法簡單了很多,代碼的可讀性提升了一個檔次,但是需要注意的在上述對容器的遍歷是只讀的,也就是說遍歷的值是不可修改的,如果需要修改其中元素,可以聲明為auto &:


   
   
  
  
          
  1. #include <iostream>
  2. #include <vector>
  3. using namespace std;
  4. int main()
  5. {
  6. std:: vector< int> vec{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
  7. cout << "修改前" << endl;
  8. for ( auto &n : vec)
  9. std:: cout << n++;
  10. cout << endl;
  11. cout << "修改后" << endl;
  12. for ( auto j : vec)
  13. std:: cout << j;
  14. cout << endl;
  15. system( "pause");
  16. return 0;
  17. }

使用時需要注意的地方

1.注意auto自動推導的類型

雖然基於范圍的for循環使用起來非常的方便,我們不用再去關注for的開始條件和結束條件等問題了,但是還是有一些細節問題在使用的時候需要注意,來看下對於容器map的遍歷:


   
   
  
  
          
  1. std:: map< string, int> map = { { "a", 1 }, { "b", 2 }, { "c", 3 } };
  2. for ( auto &val : map)
  3. 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來訪問數據。

2.注意容器本身的約束

使用基於范圍的for循環還要注意一些容器類本身的約束,比如set的容器內的元素本身有容器的特性就決定了其元素是只讀的,哪怕的使用了引用類型來遍歷set元素,也是不能修改器元素的,看下面例子:


   
   
  
  
          
  1. set< int> ss = { 1, 2, 3, 4, 5, 6 };
  2. for ( auto& n : ss)
  3. cout << n++ << endl;
上述代碼定義了一個set,使用引用類型遍歷set中的元素,然后對元素的值進行修改,該段代碼編譯失敗:error C3892: 'n' : you cannot assign to a variable that is const。同樣對於map中的first元素也是不能進行修改的。

3.當冒號后不是容器而是一個函數

再來看看假如我們給基於范圍的for循環的:冒號后面的表達式不是一個容器而是一個函數,看看函數會被調用多少次?


   
   
  
  
          
  1. #include <iostream>
  2. #include <set>
  3. using namespace std;
  4. set< int> ss = { 1, 2, 3, 4, 5, 6 };
  5. const set< int> getSet()
  6. {
  7. cout << "GetSet" << endl;
  8. return ss;
  9. }
  10. int main()
  11. {
  12. for ( auto n : getSet())
  13. cout << n << endl;
  14. system( "pause");
  15. return 0;
  16. }


可以看出,如果冒號后面的表達式是一個函數調用時,函數僅會被調用一次。

4.不要在for循環中修改容器


   
   
  
  
          
  1. #include <iostream>
  2. #include <vector>
  3. using namespace std;
  4. vector< int> vec = { 1, 2, 3, 4, 5, 6 };
  5. int main()
  6. {
  7. for ( auto n : vec)
  8. {
  9. cout << n << endl;
  10. vec.push_back( 7);
  11. }
  12. system( "pause");
  13. return 0;
  14. }
上述代碼在遍歷vector時,在容器內插入一個元素7,運行上述代碼程序崩潰了。

究其原因還是由於在遍歷容器的時候,在容器中插入一個元素導致迭代器失效了,因此,基於范圍的for循環和普通的for循環一樣,在遍歷的過程中如果修改容器,會造成迭代器失效,(有關迭代器失效的問題請參閱C++ primer這本書,寫的很詳細)也就是說基於范圍的for循環的內部實現機制還是依賴於迭代器的相關實現。


參考鏈接:http://blog.csdn.net/hailong0715/article/details/54172848


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM