1. 程式人生 > >C++菜鳥學習筆記系列(9)——迭代器

C++菜鳥學習筆記系列(9)——迭代器

C++菜鳥學習筆記系列(9)

本期主題:迭代器介紹

我們在C++菜鳥學習筆記系列(7)C++菜鳥學習筆記系列(8)中分別介紹了C++語言標準庫型別string,vector 的定義及使用。 對於string型別的物件我們可以通過範圍for語句和索引的方式訪問其中的元素;對於vector型別的物件我們也可以通過下標運算子的方式訪問其中的元素。這些方式都可以很好的幫助我們實現物件中的元素,但是對於標準庫中的其他幾種容器來說,上述的方式卻不支援。

其實除了上述的方式外C++語言還為我們提供了一種更加通用的機制迭代器為我們實現同樣的目的。這種方式對於所有的標準庫容器都可以使用。(雖然string型別並不是容器,但是string型別支援很多與容器型別相似的操作,vector型別支援下標運算子,這一點和string型別相似,string型別支援迭代器這一點和vector型別相似)。

對於迭代器而言,其物件是容器中的元素或者string物件中的某個字元。使用迭代器可以訪問某一個元素,也可以從一個元素移動到另外一個元素。

與指標類似的,迭代器也有有效和無效之分,有效的迭代器指向某個元素或者指向容器中尾元素的下一個位置,其他的所有情況都是屬於無效。

1.迭代器中的begin & end

與指標不同的是,獲取迭代器不是使用取地址符&,有迭代器的型別同時擁有返回迭代器的成員。例如,這些型別都同時擁有begin 和 end的成員。其中begin成員負責返回指向第一個元素的迭代器,end成員則負責返回指向容器尾元素下一位置的迭代器,即該迭代器指示的是容器一個本不存在的“尾後”元素。這樣的迭代器沒有什麼實際含義,只是一個標識而已,表示我們已經處理完了容器中的所有元素。end成員返回的迭代器通常被稱為尾後迭代器

或者檢查為尾迭代器

注意:特殊情況下,如果容器為空,則begin和end返回的是同一個迭代器,都為尾迭代器。

下面我們看一下迭代器的begin和 end成員具體是如何宣告的:

vector <int> v;
auto b = v.begin();
auto e = v.end();

一般來說,我們並不清楚(不在意)迭代器的準確型別是什麼。在上面的例子中使用auto關鍵字定義變數b和e,則這兩個變數的型別就是begin和 end成員的返回值型別。

2.迭代器運算子

C++語言也為我們提供了許多迭代器支援的運算子,幫助我們在實際應用中更好的使用。 下面我們就介紹一些其中比較常用的運算子。

*iter; // 返回迭代器iter所指元素的引用
iter -> mem; // 解引用iter並獲取該元素的名為mem的成員,等價於(*iter).mem
++iter; // 令iter指示容器中的下一個元素
--iter; // 令iter指示容器中的上一個元素
iter1 == iter2; // 判斷兩個迭代器是否相等
iter1 != iter2; // 判斷兩個迭代器是否不相等

這些運算子都是非常常見的,而且在其他型別的物件中也大都支援這些運算子,下面我們來看一個實際應用中的小例子:

/*
Author: wxc_1998
Date: 2018/10/2
*/

#include <iostream>
#include <vector>
#include <string>

using namespace std;

void main()
{
	string s = "hello WORLD!";
	for(auto iter = s.begin(); iter != s.end() && !isspace(*iter); ++iter )
		*iter =  toupper (*iter);
	cout << "the result is: "<< s << endl;

	cout << "press any key to continue!";
	cin.clear();
	cin.sync();
	cin.get();
}

從上述程式碼中我們可以看出我們使用了一個for迴圈用於遍歷 s 中的字元,直到我們遇到空格或者到達終點時則跳出迴圈。在這個迴圈中我們使用了迭代器每次移動到下一個元素,當然我們使用下標運算子也是可以實現相應的功能的。

小知識點:我們在C語言程式設計時常常使用 < 運算子來判斷是否到達了終點,但是在這個迴圈中我們仔細觀察一下不難發現我們使用了 != 運算子進行判斷,這是因為在C++語言中所有的容器都支援 != 運算子而只有一小部分容器支援< 運算子。同時我們後續常常使用迭代器而不使用下標運算子也與之類似,所有的容器都支援迭代器操作,但是隻有一小部分容器支援下標運算子的使用。

3.迭代器的兩種型別

一般來說我們不知道(也並不需要知道)迭代器的精確型別。實際上。那些擁有迭代器的標準庫型別使用 iterator 和 const_iterator 來表示迭代器的型別。如下所示:

vector <int>::iterator it1; // it1 可以讀寫vector <int>的元素
string ::iterator it2; // it2 可以讀寫string中的字元

vector <int>::const_iterator it3; // it3 只能讀vector <int>的元素,不能寫
string ::const_iterator it4; // it4只能讀string中的字元,不能寫

const_iterator 類似於一個常量指標,能讀取但不能修改它所指的元素值,相反iterator卻可讀可寫。

4.迭代器運算

我們在上一節中介紹了迭代器中的++和- -運算子可以幫助我們每次移動一個元素。所有的標準庫容器都支援遞增運算的迭代器,也能用==或!=進行比較等操作。而string和vector的迭代器為我們提供了更多額外的運算子,一方面可以使得迭代器的每次移動跨過多個元素,另外也支援迭代器進行關係運算。 下面我們看一下string和vector迭代器所支援的運算。 在這裡插入圖片描述

圖片來源:C++ primer 表3.7

下面我們通過一個小例子來看一下迭代器運算在二分法搜尋中的應用。

/*
Author: wxc_1998
Date: 2018/10/2
*/

#include <iostream>
#include <vector>

using namespace std;

void main()
{
	vector <int> t;
	int i;
	cout << "Please enter the integer number in order!" << endl;
	while (cin >> i) //enter ctrl + z to end
	{
		t.push_back(i);//Add an element to 't'
	}

	cin.clear();// must clear the buffer first
	cin.sync();
	int sought = 0;
	cout << "The element you want to search for :" << endl;
	cin >> sought;

	auto be = t.begin(), en = t.end();
	auto mid = t.begin() + (en-be)/2;
	
	while (be != en && *mid != sought)
	{
		if (sought < *mid)
			en = mid;
		else
			be = mid;
		mid =  be + (en-be)/2;
	}
	if (sought == *mid)
		cout << "we find the number : " << sought;
	else
		cout << "we not find the number : " << sought;

	cout << endl << "press any key to continue!" << endl;
	cin.clear();
	cin.sync();
	cin.get();
}

假如我們輸入資料: 1 23 45 51 55 67 89 92 ctrl+z 回車 23 回車 則輸出 在這裡插入圖片描述 我們在這裡主要關注的是迭代器的運算和移動操作,關於二分法演算法的原理比較簡單我這裡就不過過敘述了,大家若對二分法搜尋有什麼不明白可以自行百度瞭解。 好了,這次就寫到這裡了,我們下次再見。

注:雖然這篇部落格的內容十分簡單,但是大家若有轉載還請標明出處!

還有大家若對部落格中的內容有任何問題可以隨時聯絡我提問。

在這裡插入圖片描述