当前位置: 首页 > 编程笔记 >

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

陶超
2023-03-14
本文向大家介绍C++11的for循环,以及范围Range类的简单实现,包括了C++11的for循环,以及范围Range类的简单实现的使用技巧和注意事项,需要的朋友参考一下

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

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

与以下语句作用等价:

{ 

 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 ,其对一个数值进行封装,并重载必须的操作符。

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语句,可以更清楚的了解循环执行时具体方法的调用过程,实际用时可以删掉。

 
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_;

}; 

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

for (auto x: 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类的简单实现全部内容了,希望大家多多支持小牛知识库~

 类似资料:
  • 范围表示间隔或序列。 它用于获取位于特定范围内的一组数字/字符串。 Class 声明 (Class Declaration) 以下是com.google.common.collect.Range《C》类的声明 - @GwtCompatible public final class Range<C extends Comparable> extends Object impleme

  • 此类充当窗口小部件的基类,允许用户调整下限和上限之间的数值参数的值。 缩放窗口小部件(gtk.Hscale和gtk.Vscale)和滚动条窗口小部件(gtk.HScrollbar和gtk.VScrollbar)从Range类派生功能。 这些Range小部件与Adjustment对象一起使用。 gtk.Range类的以下重要功能由Scale和Scrollbar小部件实现 - set_update_p

  • 由于valgrind中出现了一些分段错误和警告,我发现这段代码不正确,并且在for range循环中有一些悬而未决的引用。 看起来好像开始和结束是从一个临时循环中提取的,并且在循环中丢失了。 当然,一种方法是 然而,我想知道为什么for(auto e:f()[5])是一个错误,以及是否有更好的方法或某种方法来设计f,甚至容器(

  • 问题内容: Angular确实在其HTML指令中使用数字为for循环提供了一些支持: 但是,如果您的范围变量包含具有动态数字的范围,则您每次都需要创建一个空数组。 在控制器中 在HTML中 这行得通,但是这是不必要的,因为我们根本不会在循环中使用范围数组。有谁知道设置最小值/最大值的范围或常规值? 就像是: 问题答案: 我稍微调整了这个答案,想到了这个小提琴。 过滤器定义为: 重复使用如下:

  • 问题内容: ja块变量还是局部变量?我看到j的作用域只是直到for循环结束 问题答案: 局部变量在方法,构造函数或块中声明。 由此可见, 所有块变量都是局部变量。 按照块的定义 块是平衡括号之间的一组零个或多个语句,可以在允许单个语句的任何地方使用。 所以 无论在块内声明什么变量,范围都限于该块。 因此范围仅限于该块内部。那是循环。