天天看點

C++11新特性之四:range-based for loops

熟悉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];  
           

而周遊容器類的For如下:

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++)  
    cout << *itr;  
           

不管上面哪一種方法,都必須明确的确定for循環開頭以及結尾條件,而熟悉C#或者python的人都知道在C#和python中存在一種for的使用方法不需要明确給出容器的開始和結束條件,就可以周遊整個容器,幸運的是C++11中引入了這種方法也就是基于範圍的for循環,用基于範圍的for循環改寫上面兩個例子:

int arr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };  
for (auto n : arr)  
    cout << n;  
    
std::vector<int> vec {1,2,3,4,5,6,7,8,9,10};  
for (auto n :vec)  
    std::cout << n;  
           

可以看到改寫後的使用方法簡單了很多,代碼的可讀性提升了一個檔次,但是需要注意的在上述對容器的周遊是隻讀的,也就是說周遊的值是不可修改的,如果需要修改其中元素,可以聲明為auto &:

#include <iostream>
#include <vector>
using namespace std;

int main()
{
	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;
	cout << endl;

	system("pause");
	return 0;
}
           
C++11新特性之四:range-based for loops

使用時需要注意的地方

1.注意auto自動推導的類型

雖然基于範圍的for循環使用起來非常的友善,我們不用再去關注for的開始條件和結束條件等問題了,但是還是有一些細節問題在使用的時候需要注意,來看下對于容器map的周遊:

std::map<string, int>  map = { { "a", 1 }, { "b", 2 }, { "c", 3 } };  
for (auto &val : map)  
    cout << val.first << "->" << val.second << endl;  
           

為什麼是使用val.first,val.second而不是直接輸出value呢?在周遊容器的時候,auto自動推導的類型是容器的value_type類型,而不是疊代器,而map中的value_type是std::pair,也就是說val的類型是std::pair類型的,是以需要使用val.first,val.second來通路資料。

2.注意容器本身的限制

使用基于範圍的for循環還要注意一些容器類本身的限制,比如set的容器内的元素本身有容器的特性就決定了其元素是隻讀的,哪怕的使用了引用類型來周遊set元素,也是不能修改器元素的,看下面例子:

set<int> ss = { 1, 2, 3, 4, 5, 6 };  
for (auto& n : ss)  
    cout << n++ << endl;  
           

上述代碼定義了一個set,使用引用類型周遊set中的元素,然後對元素的值進行修改,該段代碼編譯失敗:error C3892: 'n' : you cannot assign to a variable that is const。同樣對于map中的first元素也是不能進行修改的。

3.當冒号後不是容器而是一個函數

再來看看假如我們給基于範圍的for循環的:冒号後面的表達式不是一個容器而是一個函數,看看函數會被調用多少次?

#include <iostream>
#include <set>
using namespace std;

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;

	system("pause");
	return 0;
}
           
C++11新特性之四:range-based for loops

可以看出,如果冒号後面的表達式是一個函數調用時,函數僅會被調用一次。

4.不要在for循環中修改容器

#include <iostream>
#include <vector>
using namespace std;

vector<int> vec = { 1, 2, 3, 4, 5, 6 };

int main()
{
	for (auto n : vec)
	{
		cout << n << endl;
		vec.push_back(7);
	}

	system("pause");
	return 0;
}
           

上述代碼在周遊vector時,在容器内插入一個元素7,運作上述代碼程式崩潰了。

C++11新特性之四:range-based for loops

究其原因還是由于在周遊容器的時候,在容器中插入一個元素導緻疊代器失效了,是以,基于範圍的for循環和普通的for循環一樣,在周遊的過程中如果修改容器,會造成疊代器失效,(有關疊代器失效的問題請參閱C++ primer這本書,寫的很詳細)也就是說基于範圍的for循環的内部實作機制還是依賴于疊代器的相關實作。

參考連結:http://blog.csdn.net/hailong0715/article/details/54172848

繼續閱讀