C++11新特性之基本范围的For循环(range-based-for)
C++11新特性之基本范围的For循环(range-based-for)
Range-Based-For
熟悉C++98/03的对于for循环就再了解不过了,如果我们要遍历一个数组,那么在C++98/03中的实现方式:
- int arr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
- for (int i = 0; i < 10; i++)
- 	cout << arr[i];
- 	std::vector<int> vec {1,2,3,4,5,6,7,8,9,10};
- 	for (std::vector<int>::iterator itr = vec.begin(); itr != vec.end(); itr++)
- 		std::cout << *itr;
- 	std::vector<int> vec {1,2,3,4,5,6,7,8,9,10};
- 	for (auto n :vec)
- 		std::cout << n;
-  
- 	int arr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
- 	for (auto n : arr)
- 		std::cout << n;
- 	std::vector<int> vec {1,2,3,4,5,6,7,8,9,10};
- 	cout << "修改前" << endl;
- 	for (auto n :vec)
- 		std::cout << n++;
-  
- 	cout << endl;
- 	cout << "修改后" << endl;
- 	for (auto j : vec)
- 		std::cout << j;
- 修改前
- 12345678910
- 修改后
- 12345678910
- 	std::vector<int> vec {1,2,3,4,5,6,7,8,9,10};
- 	cout << "修改前" << endl;
- 	for (auto& n :vec)
- 		std::cout << n++;
-  
- 	cout << endl;
- 	cout << "修改后" << endl;
- 	for (auto j : vec)
- 		std::cout << j;
- 修改前
- 12345678910
- 修改后
- 234567891011
基于范围的For使用需要注意的细节
虽然基于范围的For循环使用起来非常的方便,我们不用再去关注for的开始条件和结束条件等问题了,但是还是有一些细节问题在使用的时候需要注意,来看下基于范围的for对于容器map的遍历:- 	std::map<string, int>  map = { { "a", 1 }, { "b", 2 }, { "c", 3 } };
- 	for (auto &val : map)
- 		cout << val.first << "->" << val.second << endl;
此外,使用基于范围的for循环还要注意一些容器类本身的约束,比如set的容器内的元素本身有容器的特性就决定了其元素是只读的,哪怕的使用了引用类型来遍历set元素,也是不能修改器元素的,看下面例子:
- 	set<int> ss = { 1, 2, 3, 4, 5, 6 };
- 	for (auto& n : ss)
- 		cout << n++ << endl;
再来看看假如我们给基于范围的for循环的:冒号都免的表达式不是一个容器而是一个函数,看看函数会被调用多少次?
- set<int> ss = { 1, 2, 3, 4, 5, 6 };
- const set<int>& getSet()
- {
- 	cout << "GetSet" << endl;
- 	return ss;
- }
-  
- int main()
- {
- 	for (auto &n : getSet())
- 		cout << n << endl;
- }
- GetSet
- 1
- 2
- 3
- 4
- 5
- 6
- 请按任意键继续. . .
最后来看看用基于范围的for循环迭代时修改容器会出现什么情况?
- vector<int> vec = { 1, 2, 3, 4, 5, 6 };
-  
- int main()
- {
- 	for (auto &n : vec)
- 	{
- 		cout << n << endl;
- 		vec.push_back(7);
- 	}
- }
 
 
究其原因还是由于在遍历容器的时候,在容器中插入一个元素导致迭代器失效了,因此,基于范围的for循环和普通的for循环一样,在遍历的过程中如果修改容器,会造成迭代器失效,(有关迭代器失效的问题请参阅C++ primer这本书,写的很详细)也就是说基于范围的For循环的内部实现机制还是依赖于迭代器的相关实现。
 
自定义的类实现基于范围的for
 
 上面说了这么多的基于范围的For的用法和使用细节,但是这些用法都用来遍历C++提供的一些数组,容器类,是否可以遍历自定义的类呢?比如在python中for可以有这种用法:for(i in range(1,10)),遍历1到10的多有元素,我们是否可以自定义一个Range类来实现相关的操作呢?答案是肯定的,下面来通过这个Range类的实现看下如果为自定义的类实现基于范围的For。 
 
 
 由于基本范围的For不需要明确指定遍历的开始和结束范围,但是在内部实现上依赖于自定义类提供的begin和end方法,此外还需要一个自定义的迭代器对象来负责范围的取值。看下面的例子: 
 
- class Myiterator
- {
- public:
- 	Myiterator(int val) :_value(val){}
- 	bool operator!=(const Myiterator& other) const
- 	{
- 		return this->_value != other._value;
- 	}
-  
- 	const int & operator*()
- 	{
- 		return _value;
- 	}
-  
- 	int& operator++()
- 	{
- 		return ++_value;
- 	}
- private:
- 	int _value;
- };
- class Range
- {
- public:
- 	Range(int begin, int end) :__begin(begin), __end(end){}
- 	Myiterator& begin()
- 	{
- 		return Myiterator(__begin);
- 	}
-  
- 	Myiterator& end()
- 	{
- 		return Myiterator(__end);
- 	}
- private:
- 	int __begin;
- 	int __end;
- };
-  
- int main()
- {
- 	for (auto i : Range(1, 10))
- 		cout << i <<  " ";
- }
  也可将上述代码改写成模板类型,但是这部分模板值适用于数值类型,如果要适合其他自定义的类,需要各位自己去改写迭代器的相关代码,是的其能够支持自定义类的++,*,!操作。各位大牛执行实现吧。 
 
- template <typename T>
- class Myiterator
- {
- public:
- 	Myiterator(T val) :_value(val){}
- 	bool operator!=(const Myiterator<T>& other) const
- 	{
- 		return this->_value != other._value;
- 	}
-  
- 	const T & operator*()
- 	{
- 		return _value;
- 	}
-  
- 	T operator++()
- 	{
- 		return ++_value;
- 	}
- private:
- 	T _value;
- };
-  
- template<typename T>
- class Range
- {
- public:
- 	Range(T begin, T end) :__begin(begin), __end(end){}
- 	Myiterator<T>& begin()
- 	{
- 		return Myiterator<T>(__begin);
- 	}
-  
- 	Myiterator<T>& end()
- 	{
- 		return Myiterator<T>(__end);
- 	}
- private:
- 	T __begin;
- 	T __end;
- };
-  
- int main()
- {
- 	for (auto i : Range<int>(1, 10))
- 		cout << i <<  " ";
- }
  因此归纳总结,为了给自定义的类实现基于范围的For的步骤如下: 
 
 1、定义自定义类的迭代器  //这里的迭代器是广义的迭代器,指针也属于该范畴。
 
 2、该类型拥有begin() 和 end() 成员方法,返回值为迭代器(或者重载全局的begin() 和 end() 函数也可以) 。
 
 3、自定义迭代器的 != 比较操作 。
 
 4、自定义迭代器的++ 前置自增操作,显然该操作要是迭代器对象指向该容器的下一个元素 。
 
5、自定义迭代器* 解引用操作,显然解引用操作必须容器对应元素的引用,否则引用遍历时将会出错
 
                    
                 
                     
                           
                     
                         
                        
 
                
            
         浙公网安备 33010602011771号
浙公网安备 33010602011771号