1.迭代器
容器迭代器的作用類似於數據庫中的遊標(cursor),它屏蔽了底層存儲空間的不連續性,在上層使容器元素維持一種“邏輯連續”的假象。不可把迭代器與void*和“基類指針”這樣的通用指針混淆。
指針代表真正的內存地址,即對象在內存中的存儲位置;而迭代器則代表元素在容器中的相對位置。
STL把迭代器劃分爲5個類別(Category),這5類迭代器分別具有不同的能力,表現爲支持不同的運算符,它們都是類模版,因此具有通用性。
標準迭代器
迭代器種類 |
提供的操作 |
特徵說明 |
trivial迭代器 |
X x; X(); *x; *x=t; X->m |
只是一個概念,用以區分所有迭代器的定義。 |
輸入迭代器 Input Iterator |
*i; (void)i++; ++i; *i++; 還包含trivial 迭代器的所有操作 |
提供只讀操作,即可讀取它所指向的元素的值,但不可改變元素的值; 如果i==j,並不意味++i==++j; |
輸出迭代器 Output Iterator |
X x; X(); X(x); X y(x); X y=x; *x=t; ++x; (void)x++; *x++=t; |
提供只寫操作,即可改變它所指向的元素的值;但不可讀取該元素的值
|
前進迭代器 Forward Iterator |
++i; i++;
|
只能向前訪問下一個元素,不能反向訪問前一個元素 典型:slist |
雙向迭代器 (Bidirectional Iterator) |
++i; i++; i--; --i; 還包含前進迭代器中所有操作 |
它是對前進迭代器的擴充,提供雙向訪問 典型:list(雙向鏈表),set/map |
隨機訪問迭代器 (Random Access Iterator) |
i+=n; i+n或n+i; i-=n; i-n; i-j; i[n]; i[n]=t; 還包含雙向迭代器中的所有操作 |
能訪問前面或後面第n個元素,即可以隨機訪問任何一個元素 典型:vector的迭代器(它就是原始指針),deque |
(2)迭代器失效及其危險性
迭代器失效是指當前容器底層存儲發生變動時,原來指向容器中某個或某些元素的迭代器由於元素的存儲位置發生了改變而不再指向它們,從而成爲無效的迭代器。使用無效的迭代器就像使用無效的野指針一樣危險。
可能引起容器存儲變動的操作:reserve()、resize()、push_back()、pop_back()、insert()、erase()、clear()等容器方法和一些泛型算法如sort()、copy()、replace()、remove()、unique(),以及集合操作(並、交、差)算法等。如下例:
/***************************************************************/
#include<iostream>
#include<vector>
using namespace std;
int main()
{
vector<int> ages; // 未預留空間
ages.push_back(2); //引起內存重分配
vector<int>::const_iterator p = ages.begin();
for (int i = 0; i<10; i++)
{
ages.push_back(5); //會引起若干次內存重分配操作
}
cout << "The first age:" << *p << endl; //p已經失效,危險!
return 0;
}
/***************************************************************/
解決迭代器失效問題:(1)在調用上述操作後重新獲取迭代器;(2)在修改容器錢爲其預留足夠的空閒空間可以避免存儲空間重分配。
上例可改爲:
/***************************************************************/
#include<iostream>
#include<vector>
using namespace std;
int main()
{
vector<int> ages; // 未預留空間
ages.push_back(2); //引起內存重分配
vector<int>::const_iterator p = ages.begin();
for (int i = 0; i<10; i++)
{
ages.push_back(5); //會引起若干次內存重分配操作
}
p = ages.begin();//重新獲取
cout << "The first age:" << *p << endl;
return 0;
}
迭代器是廣義指針,而指針滿足所有的迭代器要求。迭代器是STL算法的接口,而指針是迭代器,因此STL算法可以使用指針來對基於指針的非STL容器進行操作。例如,可將STL算法用於數組。假設要對一個double數組進行排序
double a[100];
STL sort()函數接受指向容器第一個元素的迭代器和指向超尾的迭代器作爲參數。&a[0]或a是第一個元素的地址,&a[10]或a+100是數組最後一個元素後面元素的地址,因此可以sort(a,a+100);
C++確保了表達式a+100是被定義的,只要表達式的結果位於數組中,因此C++支持將超尾概念用於數組。由於指針是迭代器,而算法是基於迭代器的,所以使得STL算法可以用於常規數組。
指針和迭代器就像是蘋果和水果的區別,蘋果屬於水果(指針屬於迭代器),但是水果並不都是蘋果。