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
- C++11 学习笔记 基于范围的for循环
- 【C++】学习笔记十七——基于范围的for循环
- 【C++学习笔记】基于范围的for循环(C++11)
- c++ 11 基于范围的for循环
- C++11 基于范围的for循环
- 基于范围的for循环(C++11)
- 基于范围的for循环
- 基于范围的for循环
- [C++] 使用基于范围的for循环操作string
- C++学习(二) 基于范围的for循环
- c++11的新特性---基于范围的for循环
- C++11 新特性(6) 基于范围的for循环
- C++11之基于范围的for循环
- C++11新特性-基于范围的for循环
- 基于范围的for循环(C++11)
- C++11:基于范围的for循环、静态断言
- C++ 基于范围的for循环
- (七十六)基于范围的for循环
- DAMAGE:After normal block(#****)错误
- Java高并发笔记
- 选课
- Android开发基本命名规范
- Qt浅谈之三十六仿360设置中心
- C++11 学习笔记 基于范围的for循环
- JWFD科技树的主干进化点
- 安装系统
- 标题
- 文件系统之mount执行流程
- C++11 学习笔记 std::function和bind绑定器
- linux下vi命令大全
- @GeneratedValue
- winrar 命令行操作汇总(持续更新)