zoukankan      html  css  js  c++  java
  • C++11的for循环,以及范围Range类的实现

    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操作符,用起来也挺方便。

  • 相关阅读:
    ECharts中悬浮图标tooltie多行显示已解决
    Linux中的$符号的三种常见用法
    Shell逐行读取文件的4种方法
    rename
    nodejs安装
    “ArcGIS Desktop遇到严重的应用程序错误,无法继续”的解决方案
    跟我一起学Redis之Redis持久化必知必会
    微服务很香--麻辣味,但要慢慢消化
    跟我一起学Redis之Redis事务简单了解一下
    跟我一起学.NetCore之EF Core 实战入门,一看就会
  • 原文地址:https://www.cnblogs.com/lidabo/p/3937208.html
Copyright © 2011-2022 走看看