C++11 学习笔记 基于范围的for循环

来源:互联网 发布:2016年各项数据 编辑:程序博客网 时间:2024/06/02 04:14

一.  基于范围的for循环简介

在C++03/98中,不同的容器和数组,遍历的方法不尽相同,写法不统一,也不够简洁,而C++11基于范围的for循环以统一,简洁的方式来遍历容器和数组,用起来更方便了。

数组循环:

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

容器循环:

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


当然,<algorithm>中还有一个for_each算法可以用来对容器进行遍历

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

 using namespace std;  void do_cout(int& num){     cout<<num<<" "; }  vector<int> vec; for_each(vec.begin(),vec.end(),do_cout);

for_each

优点:不再需要关注迭代器(Iterator)的概念

缺点:必须显示的给出容器的开头(Begin)和结尾(End) 

 

在C++11中终于有基于范围的for循环(The range-based for statement)。

 #include <iostream> #include <vector>  using namespace std;  int main(){     vector<int> arr = {1, 2, 3};     for(auto n : arr){         cout << n << endl;     }      return 0; }


在上面的基于范围的for循环中,在n的定义之后,紧跟一个冒号(:),之后直接写上需要遍历的表达式,for循环将自动以表达式返回的容器为范围进行迭代        

 需要注意

1.在上面的例子中,我们都是在使用只读方式遍历容器。如果需要在遍历时修改容器中的值,则需要如下使用引用。

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

2.若只是希望遍历,而不希望修改,可以使用const auto&来定义n的类型。这样对于复制负担比较大的容器元素(比如一个std::vector<std::string>数组)也可以无损耗地进行遍历。

 #include <iostream> #include <string> #include <vector>  using namespace std;  int main(){     vector<string> arr={"li ming","wang lei","han meimei"};     for(const auto& n : arr){         cout<<arr<<endl;     }     return 0; }

二.基于范围的for循环的使用细节

1.

 #include <iostream> #include <map>  using namespace std;  int main(){     map<string,int> ma={         {"1",1},{"2",2},{"3",3}     };     for(auto & val : mm){         cout<< val.first <<"->" << val.second <<endl;     }     return 0; }


1).for循环中val的类型是std::pair.因此,对于map这种关联性容器而言,需要使用val.first或val.second来提取键值。

2).auto自动推导师出的类型是容器中的value_type,而不是迭代器。

 

2.

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


在使用基于范围的for循环时,还需要注意容器本身的一些约束。

1).例子中,auto & 定义了std::set<int>中元素的引用,希望能够在循环中对set的值进行修改,但std::set的内部元素是只读的,因此,for循环中的auto& 会被推导为const int&。

2).在std::map的遍历中,基于范围白for循环中的std::pair引用,是不能够修改first的。

 

3.

 #include <iostream> #include <vector>  using namespace std;  vector<int> arr = { 1, 2, 3, 4, 5};  vector<int>& get_range(){     cout<<"get_range ->: "<< endl;     return arr; }  int main(){     for(auto val : get_range()){         cout<< val << endl;     }     return 0; }

输出结果:

get_range ->:

1

2

3

4

5

1).从上面的例子可以看到,无论基于范围的for循环迭代了多少次,冒号后面的表达式只会执行一次,只会在第一次迭代之前调用。

 

4.基于范围的for循环等价的普通的for循环如下:

 #include <iostream> #include <vector>  using namespace std;  int main(){     vector<int> arr = {1, 2, 3, 4, 5};      auto && __range = (arr);      for( auto __begin = __range.begin(), __end = __range.end();__begin!=__end;++__begin(){         auto val = *__begin;         cout<< val <<endl;         arr.push_back(0);     }     return 0; }

1).基于范围的for循环其实是普通for循环的语法糖,从上面的代码可以清晰地看到,和我们平时写的遍历容器不同,基于范围的for循环倾向于在循环开始之前确定好迭代的范围,而不是在每次迭代之前都去调用一次arr.end()。

 

三.让基于范围的for循环支持自定义类型

 在之前提及的vector,set,map,都是标准模板库中的容器。都实现了begin,end等函数。那么对于我们自己定义的容器类,如何才能让它支持range-based for呢?下面是书中的一个样例。

 //迭代器类的实现  namespace detail_range{  template<typename T> class iterator { public:     using value_type = T;     using size_type = size_t;      iterator(size_type cur_start, value_type begin_val, value_type step_val)             :cursor_(cur_start),step_(step_val),value_(begin_val){                 value_ += (step_ * cursor_);         }          value_type operator*() const{         return value_;     }      bool operator!=(const iterator& hrs) const{         return (cursor_ != rhs.cursor_);     }      iterator& operator++(void) //prefix ++ operator only         value_ += step_;         ++ cursor_;         return (*this);     }  private:     size_type cursor_;     const value_type step_;     value_type value_; };  }//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_的合法性。

 

 template<typename T> detail_range::imply<T> range(T end){     return { {}, end, 1 };//使用初始化列表 }  template<typename T> detail_range::impl<T> range(T begin, T end){     return { begin, end, 1}; }  template <typename T, typename U> auto range(T begin, T end, U step)->detail_range::impl<decltype(begin+step)>{     using r_t = detail_range::impl<decltype(begin + step)>;     return r_t(begin, end, step); }

 

 #include <iostream>  using namespace std;  int main(){     cout << "range(15):";     for(int i : range(15)){         cout << " " << i;     }     cout<<endl;      cout << "range(2,6):";      for( auto i : range(2, 6)){         cout << " " << i;     }     cout<<endl;      const int x = 2, y = 6, z = 3;     cout << "range(2,6,3):";     for (auto i : range(x,y,z)){         cout << " " << i;     }     cout<<endl;     cout << "range(-2,-6,-3):";     for (auto i : range(-2,-6,-3)){         cout << " " << i;     }     cout<<endl;      cout << "range(10.5,10.5):";     for (auto i : range(10.5,10.5)){         cout << " " << i;     }     cout<<endl;      cout << "range(35,27,-1):";     for (auto i : range(35,27,-1)){         cout << " " << i;     }     cout<<endl;      cout << "range(2,8,0.5):";     for (auto i : range(2,8,0.5)){         cout << " " << i;     }     cout<<endl;      cout << "range(8,7,-0.1):";     for (auto i : range(8,7,-0.1)){         cout << " " << i;     }     cout<<endl;      cout << "range('a','z'):";     for (auto i : range('a','z')){         cout << " " << i;     }     cout<<endl;      return 0; }  //示例运行的结果如下: //range(15): 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 //range(2, 6): 2 3 4 5 //range(2, 6, 3): 2 5 //range(-2, -6, -3): -2 -5 //range(10.5, 10.5): 10.5 11.5 12.5 13.5 14.5 //range(35, 27, -1): 35 34 33 32 31 30 29 28 //range(2, 8, 0.5): 2 2.5 3 3.5 4 4.5 5 5.5 6 6.5 7 7.5 //range(8, 7, -0.1): 8 7.9 7.8 7.7 7.6 7.5 7.4 7.3 7.2 7.1 //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 

 

0 0
原创粉丝点击