【C++】STL迭代器

作者前言:學習迭代器之前,我們要先清楚,什麼是迭代器?迭代器是幹什麼用的?爲什麼要使用迭代器?

迭代器是一個支持指針類型抽象的類對象。提供了一種一般化的方法,對順序或關聯容器類型中的每個元素進行連續訪問。

簡單來說,迭代器就是用來遍歷容器的工具,並可以對容器進行一定的操作。

劃重點!!!

迭代器和指針有什麼區別?

迭代器不是指針,是類模板,表現的像指針。他只是模擬了指針的一些功能,通過重載了指針的一些操作符 *、++、--、&等。迭代器封裝了指針,是一個可遍歷STL容器內全部或部分元素的對象,本質是封裝了原生指針,是指針概念的一種提升,提供了比指針更高級的行爲,相當於一種智能指針。

迭代器返回的是對象引用而不是對象的值,所以const只能輸出迭代器使用*取值後的值而不能直接輸出其自身。

       或許會有人說:我不用迭代器也一樣可以遍歷容器,爲什麼還要用迭代器

       我們現在常用的容器vector,list,map,set,multiset,multimp,deque這幾個容器的內部實現由順序表,鏈表,紅黑樹。所以我們要遍歷這些容器就要先知道它的底層結構,這樣就很麻煩。而iterator就是在底層定義好,任何容器都是同樣的遍歷方式,這樣就算我們不知道底層實現也可以操作容器。

所以Iterator的使用可以讓我們在不知道對象內部表示的情況下,按照一定順序(由iterator提供的方法)訪問聚合對象中的各個元素。

迭代器的作用:能夠讓迭代器與算法不干擾的相互發展,最後又能無間隙的粘合起來,重載了*,++,==,!=,=運算符。用以操作複雜的數據結構,容器提供迭代器,算法使用迭代器;常見的一些迭代器類型有iterator、const_iterator、reverse_iterator和const_reverse_iterator。

舉例講解迭代器:

假設iter爲任意容器類型的一個iterator,則:

   ++iterator;//向前移動迭代器,使其指向容器的下一個元素

   *iterator;//返回iterator指向元素的值

每種容器類型都提供一個begin()和一個end()成員元素。

   begin()返回一個iterator,它指向容器的第一個元素

   end()返回一個iterator,它指向容器的末元素的下一個位置

由於模板和嵌套類語法的原因 iterator 的定義看起來有點嚇人,例如下面是一對 iterator的定義 它們指向一個內含 string 元素的 vector:

// vector<string> vec;

vector<string>::iterator iter = vec.begin();

vector<string>::iterator iter_end = vec.end();

爲了把每個 string 元素打印到標準輸出上 我們可以這樣寫

for( ; iter != iter_end; ++iter )

cout << *iter << '\n'; 

當然,這裏 *iter 的運算結果就是實際的 string 對象。

除了iterator類型,每個容器還定義了一個 const iterator類型,對於遍歷const容器是必需的,const iterator 允許以只讀方式訪問容器的底層元素。分析下面給出的一個例子

// 無法通過編譯

template < typename type >

int count( const vector< type > &vec, type value )

int count = 0; 

vector< type >::iterator iter = vec.begin(); 

while ( iter != vec.end() )

{  

if ( *iter == value )   

++count;

++iter;

return count;

}

    爲什麼不能編譯呢?思考一下

    問題在於vec是一個const引用,但是我們試圖把一個非 const 的iterator綁定在它上面。如果允許這樣做,那就沒有什麼能阻止我們在後面通過iterator修改這個vector的值了。

爲了防止這種情況的出現,C++語言要求綁定在onst vector上的iterator也必須是const iterator。我們這樣做

// ok: 這次可以通過編譯了

vector< type >::const_iterator iter = vec.begin();     

const容器只能被綁定在 const iterator 上 這樣的要求與 const 指針只能指向 const 數組的 行爲一樣 在兩種情況下 C++語言都努力保證 const 容器的內容不會被改變。

下面我自己模擬一個vector容器,然後實現一個迭代器來操作這個容器

template<typename T>
class Vector;
template<typename T>
class Iterator
{
public:
	Iterator(Vector<T>* pv, int idx) :
		pvec(pv), index(idx)
	{}
	bool operator!=(const Iterator left)
	{
		return index != left.index;
	}
	T& operator*()const;
	const Iterator operator++(int)
	{
		const Iterator tmp(*this);
		index++;
		return tmp;
	}
	const Iterator operator--(int)
	{
		const Iterator tmp(*this);
		index--;
		return tmp;
	}
	const Iterator operator-(int left)
	{
		return Iterator(pvec, index - left);
	}
private:
	Vector<T>* pvec;
	int index;
};
template<typename T>
class Vector
{
public:
	typedef Iterator<T> iterator;
	Vector()
	{
		parr = new T[2]();
		cursize = 0;
		totalsize = 2;
	}
	iterator begin()
	{
		return iterator(this, 0);
	}
	iterator end()
	{
		return iterator(this, cursize);
	}
	void push_back(T data)
	{
		insert(end(), data);
	}
	void insert(iterator _where, T data)
	{
		if (full())
		{
			resize();
		}
		for (iterator it = end(); it != _where; it--)
		{
			*it = *(it - 1);
		}
		*_where = data;
		cursize++;
	}
	~Vector()
	{
		delete[] parr;
		parr = NULL;
	}
	void resize()
	{
		T* pnewspace = new T[totalsize * 2];
		memcpy(pnewspace, parr, sizeof(T)*totalsize);
		delete[] parr;
		parr = pnewspace;
		totalsize *= 2;
	}
	
	//arr[0] = 10;
	T& operator[](int index)
	{
		return parr[index];
	}
private:
	bool full()
	{
		return cursize == totalsize;
	}
	T* parr;
	int cursize;//當前元素個數  
	int totalsize;//總大小  
};
template<typename T>
T& Iterator<T>::operator*()const
{
	return (*pvec)[index]; //vec[0];
}

測試一下.......

#include<vector>
int main()
{
	Vector<int> vec;
	for (int i = 0; i < 10; i++)
	{
		vec.push_back(i + 1);
	}
	Vector<int>::iterator it = vec.begin();
	while (it != vec.end())
	{
		std::cout << *it << " ";
		it++;
	}
	std::cout << std::endl;
	return 0;
}

 運行結果!!

最後說一下STL迭代器刪除元素的問題

這個主要考察的是迭代器失效的問題。

1.對於序列容器vector,deque來說,使用erase(iterator)後,後邊的每個元素的迭代器都會失效,但是後邊每個元素都會往前移動一個位置,但是erase會返回下一個有效的迭代器。

2.對於關聯容器map,set來說,使用了erase(iterator)後,當前元素的迭代器失效,但是其結構是紅黑樹,刪除當前元素不會影響到下一個元素的迭代器,所以在調用erase之前,記錄下一個元素的迭代器即可。

3.對於list來說,它使用了不連續分配的內存,並且它的erase方法也會返回下一個有效的iterator,因此上面兩種正確的方法都可以使用。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章