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 (int x: 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
class FakeIter
{
    typedef long _VType;  //数值的类型为long。当然了,也可以写个模板出来
public:
 
    explicit FakeIter(_VType val)
        :value_(val){}
 
    bool operator != (const FakeIter& other) const
    {
        return (this->GetValue()) != (other.GetValue());
    }
    _VType operator* () const
    {
        return GetValue();
    }
    const FakeIter& operator++ ()
    {
        ++value_;
        return *this;
    }
private:
    _VType GetValue() const
    {
        return value_;
    }
    _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
class Range
{
    typedef long _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;
        return FakeIter(begin_value_);
    }
    FakeIter end () const
    {
        cout<<"Range::end()"<<endl;
        return FakeIter(end_value_ );
    }
private:
    _VType begin_value_;
    _VType end_value_;
};

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

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

在vs2012及clang下输出如下:

Range::Range() 
Range::begin() 
Range::end() 




Range::~Range() 
Loop end

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

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

C++11的for循环,以及范围Range类的实现的更多相关文章

  1. C++11之for循环的新用法

    C++使用如下方法遍历一个容器: #include "stdafx.h" #include<iostream> #include<vector> int m ...

  2. python循环for,range,xrange;while

    >>>range(1,5)#代表从1到5(不包含5) [1,2,3,4] >>>range(1,5,2)#代表从1到5,间隔2(不包含5) [1,3] >&g ...

  3. C++11之for循环的新用法《转》

    相关资料:https://legacy.gitbook.com/book/changkun/cpp1x-tutorial/details C++11之for循环的新用法 C++使用如下方法遍历一个容器 ...

  4. 第二天:python的函 数、循环和条件、类

    https://uqer.io/community/share/54c8af17f9f06c276f651a54 第一天学习了Python的基本操作,以及几种主要的容器类型,今天学习python的函数 ...

  5. Android的消息循环机制 Looper Handler类分析

    Android的消息循环机制 Looper Handler类分析 Looper类说明   Looper 类用来为一个线程跑一个消息循环. 线程在默认情况下是没有消息循环与之关联的,Thread类在ru ...

  6. C++ 实现Range类,用于常规遍历

    PYTHON的Range类非常好用,所以用C++来简单实现下:  // 实现Range类,用于遍历 // #include <string> class Range { public: / ...

  7. 【TODO】Ruby Range类

    创建: 2017/10/28   Range类                                                                             ...

  8. Kotlin 区间和循环 Loop和Range

    在kotln中有一种很简单的方式来表达某一个区间的数字. 比如我想存储1~100. 我在其他的语言中可以用数组,步骤大概有三个把. 但是在kotlin中就只需要一步. 代码如下: fun main(a ...

  9. 第11月第11天 avplayer循环播放

    1. /* Setting actionAtItemEnd to None prevents the movie from getting paused at item end. A very sim ...

随机推荐

  1. Cocos2d-x实例:单点触摸事件

    addChild(boxC,30, kBoxC_Tag);                                                                        ...

  2. OC1_点语法

    // // Dog.h // OC1_点语法 // // Created by zhangxueming on 15/6/16. // Copyright (c) 2015年 zhangxueming ...

  3. Java 对于继承的初级理解

    概念:继承,是指一个类的定义可以基于另外一个已存在的类,即子类继承父类,从而实现父类的代码的重用.两个类的关系:父类一般具有各个子类共性的特征,而子类可以增加一些更具个性的方法.类的继承具有传递性,即 ...

  4. [GeekBand] 探讨C++新标准之新语法——C++ 11~14

    一. 可变参数模板(Variadic Templates) 在C++11中,出现了参数数目可变的模板,这部分在之前C++高级编程的时候就有学习到. 其实,在C中就有类似的设定.最常用的printf() ...

  5. [GeekBand] 面向对象的设计模式(C++)(2)

    本篇笔记紧接上篇,继续学习设计模式. 4. 对象创建类设计模式 通过对象创建模式绕开new,来避免对象创建(new)过程中所导致的紧耦合,从而支持对象创建的稳定.它是接口抽象之后的第一步工作. 4.1 ...

  6. ZigBee组网原理

    Zigbee组建一个完整的网络包含两个步骤:网络初始化和节点加入网络.其中,节点加入网络可以分为通过协调器直接连接入网和通过已有父节点入网.下面来依次说明. 1. 网络初始化 ZigBee网络初始化只 ...

  7. Bind安装配置及应用

    Bind安装配置及应用 BIND:Berkeley Internet Name Domain ,ISC.org     DNS服务的实现:     监听端口:53/UDP , 53/TCP     程 ...

  8. 锋利的jquery-validation

    jquery插件 jquery插件项目托管于gitHub,项目地址https://github.com/jquery/plugins.jquery.com jquery插件的使用 表单验证插件 现在网 ...

  9. java实现多模匹配算法

    这个是好几年前写的了.都统一放到cnblogs上面. --------------------------------Node ---------------------------------- p ...

  10. jquery ListBox 左右移动

    <head runat="server"> <title>无标题页</title> <script type="text/jav ...