作者前言:學習迭代器之前,我們要先清楚,什麼是迭代器?迭代器是幹什麼用的?爲什麼要使用迭代器?
迭代器是一個支持指針類型抽象的類對象。提供了一種一般化的方法,對順序或關聯容器類型中的每個元素進行連續訪問。
簡單來說,迭代器就是用來遍歷容器的工具,並可以對容器進行一定的操作。
劃重點!!!
迭代器和指針有什麼區別?
迭代器不是指針,是類模板,表現的像指針。他只是模擬了指針的一些功能,通過重載了指針的一些操作符 *、++、--、&等。迭代器封裝了指針,是一個可遍歷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,因此上面兩種正確的方法都可以使用。