程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> C++11 學習筆記 基於范圍的for循環,學習筆記for

C++11 學習筆記 基於范圍的for循環,學習筆記for

編輯:C++入門知識

C++11 學習筆記 基於范圍的for循環,學習筆記for


一.  基於范圍的for循環簡介

在C++03/98中,不同的容器和數組,遍歷的方法不盡相同,寫法不統一,也不夠簡潔,而C++11基於范圍的for循環以統一,簡潔的方式來遍歷容器和數組,用起來更方便了。

數組循環:

1 using namespace std;
2 
3 const int size = 5;
4 int* p = new int[size]{1,2,3,4,5};
5 for(int i =0;i<size;i++){
6     cout<<p[i]<<" ";
7 }

容器循環:

1 using namespace std;
2 
3 vector<int> vec;
4 for (auto it=vec.begin(),it!=vec.end();it++){
5     cout<<*it<<" ";
6 }

 

當然,<algorithm>中還有一個for_each算法可以用來對容器進行遍歷

Function for_each( InputIterator begin, InputIterator end, Function f ) {
    while ( begin != end )
        f( *begin++ );
}

 

1 using namespace std;
2 
3 void do_cout(int& num){
4     cout<<num<<" ";
5 }
6 
7 vector<int> vec;
8 for_each(vec.begin(),vec.end(),do_cout);

for_each

優點:不再需要關注迭代器(Iterator)的概念

缺點:必須顯示的給出容器的開頭(Begin)和結尾(End) 

 

在C++11中終於有基於范圍的for循環(The range-based for statement)。

 1 #include <iostream>
 2 #include <vector>
 3 
 4 using namespace std;
 5 
 6 int main(){
 7     vector<int> arr = {1, 2, 3};
 8     for(auto n : arr){
 9         cout << n << endl;
10     }
11 
12     return 0;
13 }

 

在上面的基於范圍的for循環中,在n的定義之後,緊跟一個冒號(:),之後直接寫上需要遍歷的表達式,for循環將自動以表達式返回的容器為范圍進行迭代        

 需要注意

1.在上面的例子中,我們都是在使用只讀方式遍歷容器。如果需要在遍歷時修改容器中的值,則需要如下使用引用。

1 for ( auto& n : arr){
2     cout<< n++ << endl;
3 }

 2.若只是希望遍歷,而不希望修改,可以使用const auto&來定義n的類型。這樣對於復制負擔比較大的容器元素(比如一個std::vector<std::string>數組)也可以無損耗地進行遍歷。

 1 #include <iostream>
 2 #include <string>
 3 #include <vector>
 4 
 5 using namespace std;
 6 
 7 int main(){
 8     vector<string> arr={"li ming","wang lei","han meimei"};
 9     for(const auto& n : arr){
10         cout<<arr<<endl;
11     }
12     return 0;
13 }

 

二.基於范圍的for循環的使用細節

1.

 1 #include <iostream>
 2 #include <map>
 3 
 4 using namespace std;
 5 
 6 int main(){
 7     map<string,int> ma={
 8         {"1",1},{"2",2},{"3",3}
 9     };
10     for(auto & val : mm){
11         cout<< val.first <<"->" << val.second <<endl;
12     }
13     return 0;
14 }

 

1).for循環中val的類型是std::pair.因此,對於map這種關聯性容器而言,需要使用val.first或val.second來提取鍵值。

2).auto自動推導師出的類型是容器中的value_type,而不是迭代器。

 

2.

 1 #include <iostream>
 2 #include <set>
 3 
 4 using namespace std;
 5 
 6 int main(){
 7     set<int> se = {1 , 2, 3};
 8     for (auto& val : ss){
 9       //error:increment of read-only reference 'val'
10       cout<< val++ << Lendl;
11     }
12     return 0;
13 }    

 

在使用基於范圍的for循環時,還需要注意容器本身的一些約束。

1).例子中,auto & 定義了std::set<int>中元素的引用,希望能夠在循環中對set的值進行修改,但std::set的內部元素是只讀的,因此,for循環中的auto& 會被推導為const int&。

2).在std::map的遍歷中,基於范圍白for循環中的std::pair引用,是不能夠修改first的。

 

3.

 1 #include <iostream>
 2 #include <vector>
 3 
 4 using namespace std;
 5 
 6 vector<int> arr = { 1, 2, 3, 4, 5};
 7 
 8 vector<int>& get_range(){
 9     cout<<"get_range ->: "<< endl;
10     return arr;
11 }
12 
13 int main(){
14     for(auto val : get_range()){
15         cout<< val << endl;
16     }
17     return 0;
18 }

 輸出結果:

get_range ->:

1

2

3

4

5

1).從上面的例子可以看到,無論基於范圍的for循環迭代了多少次,冒號後面的表達式只會執行一次,只會在第一次迭代之前調用。

 

4.基於范圍的for循環等價的普通的for循環如下:

 1 #include <iostream>
 2 #include <vector>
 3 
 4 using namespace std;
 5 
 6 int main(){
 7     vector<int> arr = {1, 2, 3, 4, 5};
 8 
 9     auto && __range = (arr);
10      for( auto __begin = __range.begin(), __end = __range.end();__begin!=__end;++__begin(){
11         auto val = *__begin;
12         cout<< val <<endl;
13         arr.push_back(0);
14     }
15     return 0;
16 }

1).基於范圍的for循環其實是普通for循環的語法糖,從上面的代碼可以清晰地看到,和我們平時寫的遍歷容器不同,基於范圍的for循環傾向於在循環開始之前確定好迭代的范圍,而不是在每次迭代之前都去調用一次arr.end()。

 

三.讓基於范圍的for循環支持自定義類型

 在之前提及的vector,set,map,都是標准模板庫中的容器。都實現了begin,end等函數。那麼對於我們自己定義的容器類,如何才能讓它支持range-based for呢?下面是書中的一個樣例。

 1 //迭代器類的實現
 2 
 3 namespace detail_range{
 4 
 5 template<typename T>
 6 class iterator
 7 {
 8 public:
 9     using value_type = T;
10     using size_type = size_t;
11 
12     iterator(size_type cur_start, value_type begin_val, value_type step_val)
13             :cursor_(cur_start),step_(step_val),value_(begin_val){
14                 value_ += (step_ * cursor_);
15         }
16     
17     value_type operator*() const{
18         return value_;
19     }
20 
21     bool operator!=(const iterator& hrs) const{
22         return (cursor_ != rhs.cursor_);
23     }
24 
25     iterator& operator++(void) //prefix ++ operator only
26         value_ += step_;
27         ++ cursor_;
28         return (*this);
29     }
30 
31 private:
32     size_type cursor_;
33     const value_type step_;
34     value_type value_;
35 };
36 
37 }//namespace detail_range

C++11,定義模板的別名只能使用using。定義一般類型的別名時與typedef沒有區別。

//impl為要實現的類似容器的類,我們給它定義了容器所要擁有的基本的概念抽象

namespace detail_range{

template<typename T>
class imply
{
public:
    using value_type = T;
    using reference = const value_type&;
    using const_reference = const value_type&;
    using iterator = const detail_range::iterator<value_type>;
    using const_iterator = const detail_range::iterator<value_type>;
    using size_type = typename iterator::size_type;

    impl(value_type begin_val, value_type end_val, value_type step_val)
        : begin_(begin_val),end_(end_val),step_(step_val),max_count_(get_adjusted_count()){}

    size_type size(void) const{
        return max_count_;
    }

    const_iterator begin(void) const{
        return { 0, begin_, step_ };
    }

    const_iterator end(void) const{
        return {max_count_ , begin_, step_ };
    }

private:
    const value_type begin_;
    const value_type end_;
    const value_type step_;
    const size_type max_count_;

    size_type get_adjusted_count(void) const{
        if(step_ > 0 && begin_ >= end_)
            throw std::logic_error("End value must be greater than begin value.");
        else if(step_ < 0 && begin_ <= end_)
                throw std::logic_error("End value must be less than begin value.");

        size_type x = static_cast<size_type>((end_ - begin_)/step_);
        if( begin_+ (step_ * x) != end_)
            ++ x;
        return x;
    }

};

}//namespace detail_range

max_count_為最大迭代次數,通過調用get_adjusted_count函數獲得,他會先判斷begin_,end_和step_的合法性。

 

 1 template<typename T>
 2 detail_range::imply<T> range(T end){
 3     return { {}, end, 1 };//使用初始化列表
 4 }
 5 
 6 template<typename T>
 7 detail_range::impl<T> range(T begin, T end){
 8     return { begin, end, 1};
 9 }
10 
11 template <typename T, typename U>
12 auto range(T begin, T end, U step)->detail_range::impl<decltype(begin+step)>{
13     using r_t = detail_range::impl<decltype(begin + step)>;
14     return r_t(begin, end, step);
15 }

 

 

 1 #include <iostream>
 2 
 3 using namespace std;
 4 
 5 int main(){
 6     cout << "range(15):";
 7     for(int i : range(15)){
 8         cout << " " << i;
 9     }
10     cout<<endl;
11 
12     cout << "range(2,6):";
13      for( auto i : range(2, 6)){
14         cout << " " << i;
15     }
16     cout<<endl;
17 
18     const int x = 2, y = 6, z = 3;
19     cout << "range(2,6,3):";
20     for (auto i : range(x,y,z)){
21         cout << " " << i;
22     }
23     cout<<endl;
24 
25     cout << "range(-2,-6,-3):";
26     for (auto i : range(-2,-6,-3)){
27         cout << " " << i;
28     }
29     cout<<endl;
30 
31     cout << "range(10.5,10.5):";
32     for (auto i : range(10.5,10.5)){
33         cout << " " << i;
34     }
35     cout<<endl;
36 
37     cout << "range(35,27,-1):";
38     for (auto i : range(35,27,-1)){
39         cout << " " << i;
40     }
41     cout<<endl;
42 
43     cout << "range(2,8,0.5):";
44     for (auto i : range(2,8,0.5)){
45         cout << " " << i;
46     }
47     cout<<endl;
48 
49     cout << "range(8,7,-0.1):";
50     for (auto i : range(8,7,-0.1)){
51         cout << " " << i;
52     }
53     cout<<endl;
54 
55     cout << "range('a','z'):";
56     for (auto i : range('a','z')){
57         cout << " " << i;
58     }
59     cout<<endl;
60 
61     return 0;
62 }
63 
64 //示例運行的結果如下:
65 //range(15): 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14
66 //range(2, 6): 2 3 4 5
67 //range(2, 6, 3): 2 5
68 //range(-2, -6, -3): -2 -5
69 //range(10.5, 10.5): 10.5 11.5 12.5 13.5 14.5
70 //range(35, 27, -1): 35 34 33 32 31 30 29 28
71 //range(2, 8, 0.5): 2 2.5 3 3.5 4 4.5 5 5.5 6 6.5 7 7.5
72 //range(8, 7, -0.1): 8 7.9 7.8 7.7 7.6 7.5 7.4 7.3 7.2 7.1
73 //range('a', 'z'): a b c d e f g h i j k l m n o p q r s t u v w x y 

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved