C++11的for轮回,以及局限Range类的实现
添加时间:2013-6-2 点击量:
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_;
};
至于“容器”类的实现,就更简单了。类中的办法加了一些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支撑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_;
};
至于“容器”类的实现,就更简单了。类中的办法加了一些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操纵符,用起来也挺便利。
彼此相爱,却不要让爱成了束缚:不如让它成为涌动的大海,两岸乃是你们的灵魂。互斟满杯,却不要同饮一杯。相赠面包,却不要共食一个。一起歌舞欢喜,却依然各自独立,相互交心,却不是让对方收藏。因为唯有生命之手,方能收容你们的心。站在一起却不要过于靠近。—— 纪伯伦《先知》