C++11的for循环,以及范围Range类的实现

摘要:
且由于__range变量是右值引用,如果range_expression的结果是右值,其将会在循环结束后析构。这样,C++11终于支持了这种现代编程语言都支持的遍历方式了。但是,无论是语法还是标准库都不支持对具体数字的遍历,比如python中的foriinxrange(1,5)语句中,x将连续取[1,4]中的值。最直接的方法,就是写一个函数,返回一个vector对象,其元素为从begin到end的值。在for循环的初始化部分,调用了这两个方法之后,就没这个类啥事了——都是迭代器的事。配合C++11lambda操作符,用起来也挺方便。

C++11支持range-based for循环。这是一个很方便的特性,能省挺多代码。以下代码就能很方便的遍历vector中的元素,并打印出来:

1
2
3
4
5
6
7
8
std::vector<int> int_vec;
int_vec.push_back(1);
int_vec.push_back(2);
//如果要修改int_vec中的元素,将变量x声明为 int& 即可
for(intx: int_vec)
{
std::cout << x << endl;
}

可以遍历的对象包括:

  • 数组。(不包括指针)
  • 定义了begin()和end()方法,且返回该方法返回迭代器的类对象。(STL 中所有容器都可以)

(对于动态生成的数组的遍历,用下面介绍的Range类也能省不少代码)

参考http://en.cppreference.com/w/cpp/language/range-for可知,

语句for (range_declaration:range_expression)loop_statement

与以下语句作用等价:

1
2
3
4
5
6
7
8
9
{
auto&& __range = range_expression ;
for(auto__begin = begin_expr,
__end = end_expr;
__begin != __end; ++__begin) {
range_declaration = *__begin;
loop_statement
}
}

对于可遍历的类对象,__begin和__end分别由类的begin()和end()方法产生。且由于__range变量是右值引用,如果range_expression的结果是右值,其将会在循环结束后析构。


这样,C++11终于支持了这种现代编程语言都支持的遍历方式了。但是,无论是语法还是标准库都不支持对具体数字的遍历,比如python中的 for i in xrange(1,5)语句中,x将连续取[1,4]中的值。(Boost库有irange类可以满足这个需求,但是下面会讨论下我的实现)

最直接的方法,就是写一个函数,返回一个vector<int>对象,其元素为从begin到end的值。但这样每次循环时都得构造一个这样的对象,略慢。

从标准来看,如果一个类要支持这样遍历,至少得有begin()和end()方法。在for循环的初始化部分,调用了这两个方法之后,就没这个类啥事了——都是迭代器的事。所以很自然,从迭代器上下手。这个迭代器必须支持三种操作:!=,前缀++,解引用。有没发现,如果这个“迭代器”是个int数值的话,上面循环中的__begin!=__end;++__begin语句就是一个非常自然的实现。现在的目标很简单了:这个“迭代器”不遍历容器中的每个元素,而就是一个简单的int数值的封装。对其解引用将返回这个数,而比较和自加操作均对这个数进行操作。


有想法之后,实现起来就很容易了。首先定义一个仿迭代器 FakeIter ,其对一个数值进行封装,并重载必须的操作符。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
classFakeIter
{
typedeflong_VType; //数值的类型为long。当然了,也可以写个模板出来
public:
explicitFakeIter(_VType val)
:value_(val){}
booloperator != (constFakeIter& other) const
{
return(this->GetValue()) != (other.GetValue());
}
_VType operator* () const
{
returnGetValue();
}
constFakeIter& operator++ ()
{
++value_;
return*this;
}
private:
_VType GetValue() const
{
returnvalue_;
}
_VType value_;
};

至于“容器”类的实现,就更简单了:实现begin()和end()方法,并返回上面的FakeIter就好了。类中的方法加了一些cout语句,可以更清楚的了解循环执行时具体方法的调用过程,实际用时可以删掉。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
classRange
{
typedeflong_VType; //同样,也可以弄个模板出来,但是就不方便用了
public:
Range (_VType begin_v, _VType end_v)
:begin_value_(begin_v), end_value_(end_v)
{
cout<<"Range::Range()"<<endl;
}
~Range()
{
cout<<"Range::~Range()"<<endl;
}
FakeIter begin () const
{
cout<<"Range::begin()"<<endl;
returnFakeIter(begin_value_);
}
FakeIter end () const
{
cout<<"Range::end()"<<endl;
returnFakeIter(end_value_ );
}
private:
_VType begin_value_;
_VType end_value_;
};

好了,试试看这货有没用:

1
2
3
4
5
for(autox: Range(1,5))
{
std::cout<<x<<endl;
}
std::cout<<"Loop end"<<endl;

在vs2012及clang下输出如下:

Range::Range()
Range::begin()
Range::end()
1
2
3
4
Range::~Range()
Loop end

嗯,如果把那些影响视线的输出语句的注释掉的话貌似能用了。现在要遍历一个new生成的数组的话,只需用这东西Range个下标,世界就清静了。

但是,步长呢!好吧,貌似我暂时还没这方面的需求。不过实现起来也很简单:修改FakeIter类就可以了,可以增加一个表示步长的成员,然后再修改自加操作。更进一步,也可以加一个生成器方法,就能变成一个更通用的生成器了。配合C++11 lambda操作符,用起来也挺方便。

免责声明:文章转载自《C++11的for循环,以及范围Range类的实现》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇apache maven安装教程synchronized实现原理及锁优化下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

shell for循环 多个变量

需求:需要输出以下2开头的端口号和其对应的文件like: port and port_k8s_xxx.conf 其脚本为: #! /bash/shell #以value_name=(value1 value2 value3)的形式定义数组 a=(`ls |grep -v ^1|grep -v 22281_k8s_qkd_http.conf|grep -v...

Sqlserver 游标 慢

.net项目中有个模块做统计功能,原先方法速度很慢,所以需要改进 原先的处理方式是,这些数据分别涉及到四五张表,前台从数据库中查询出需要的数据集,然后分别遍历这些数据集拼接html字符串显示在界面上。 优化思考:由于前台需要多次调用数据库,试想把改功能封装成一个存储过程实现,前台负责传输参数,有存储过程计算拼接之后返回html结果。其实对于这样统计为什么会...

TypeScript ES6-Promise 递归遍历文件夹中的文件

貌似很多人都爱用这个作为写文章的初尝试,那来吧。遍历文件夹下的所有文件,如遍历文件夹下并操作HTML/CSS/JS/PNG/JPG步骤如下:1、传入一个路径,读取路径里面所有的文件;2、遍历读取的文件,判断当前文件是文件还是文件夹;3、if: 前目录为文件,输出当前文件绝对路径,return;4、else: 当前目录为文件夹,获取文件夹路径,继续递归遍历该...

【看书】关于for循环中定义变量的后续讨论

想想除了看书时之外 有时也会幸运地有点想法 所以再开一个类别来记录吧~ 在下希望这个分类下的博文越多越好 -.- ——中午记录——— 晚上有场个人赛 感觉略紧张啊…… 万一爆零肿么办 -_- 感觉晚上还会来编辑一次 先立个flag 【关于for循环中定义变量的后续讨论】 上次在书中看到了关于for循环中定义的变量问题1【看书】for循环中的定...

Delphi调用IE打开网页

//调用相应程序打开   uses ShellAPI;   procedure TForm1.Button1Click(Sender: TObject);   begin   //用IE打开   ShellExecute(Handle, 'open', 'IExplore.EXE', 'about:blank', nil, SW_SHOWNORMAL);...

shell 中的for、while循环及if语句

shell与其他语言一样也支持for、while循环 for循环的一般格式如下: 1 #!/bin/sh 2 3 for 变量 in 列表 4 do 5 command 1 6 command 2 7 command 1 8 ......... 9 command n 10 do...