C++ STL与迭代器
將容器類模板實例化時,會指明容器中存放的元素是什么類型的:可以存放基本類型的變量,也可以存放對象。
對象或基本類型的變量被插入容器中時,實際插入的是對象或變量的一個復制品。
STL 中的許多算法(即函數模板),如排序、查找等算法,在執行過程中會對容器中的元素進行比較。這些算法在比較元素是否相等時通常用運算符進行,比較大小通常用<運算符進行,因此,被放入容器的對象所屬的類最好重載==和<運算符,以使得兩個對象用==和<進行比較是有定義的。
順序容器
順序容器有以下三種:可變長動態數組 vector、雙端隊列 deque、雙向鏈表 list。
它們之所以被稱為順序容器,是因為元素在容器中的位置同元素的值無關,即容器不是排序的。
關聯容器
關聯容器有以下四種:set、multiset、map、multimap。關聯容器內的元素是排序的。插入元素時,容器會按一定的排序規則將元素放到適當的位置上,因此插入元素時不能指定位置。
默認情況下,關聯容器中的元素是從小到大排序(或按關鍵字從小到大排序)的,而且用<運算符比較元素或關鍵字大小。因為是排好序的,所以關聯容器在查找時具有非常好的性能。
棧/隊列
除了以上兩類容器外,STL 還在兩類容器的基礎上屏蔽一部分功能,突出或增加另一部分功能,實現了三種容器適配器:棧 stack、隊列 queue、優先級隊列 priority_queue。
概述
容器都是類模板。它們實例化后就成為容器類。用容器類定義的對象稱為容器對象。
例如,vector<int>是一個容器類的名字,vector<int> a;就定義了一個容器對象 a,a 代表一個長度可變的數組,數組中的每個元素都是 int 類型的變量;
任何兩個容器對象,只要它們的類型相同,就可以用 <、<=、>、>=、==、!= 進行詞典式的比較運算。假設 a、b 是兩個類型相同的容器對象,這些運算符的運算規則如下。
- a == b:若 a 和 b 中的元素個數相同,且對應元素均相等,則a == b的值為 true,否則值為 false。元素是否相等是用==運算符進行判斷的。
- a<b:規則類似于詞典中兩個單詞比較大小,從頭到尾依次比較每個元素,如果發生 a 中的元素小于 b 中的元素的情況,則a<b的值為 true;如果沒有發生 b 中的元素小于 a 中的元素的情況,且 a 中的元素個數比 b 少,a<b的值也為 true;其他情況下值為 false。元素比較大小是通過<運算符進行的。
- a > b:等價于 b < a。
?
所有容器都有以下兩個成員函數:
- int size():返回容器對象中元素的個數。
- bool empty():判斷容器對象是否為空。
順序容器和關聯容器還有以下成員函數:
- begin():返回指向容器中第一個元素的迭代器。
- end():返回指向容器中最后一個元素后面的位置的迭代器。
- rbegin():返回指向容器中最后一個元素的反向迭代器。
- rend():返回指向容器中第一個元素前面的位置的反向迭代器。
- erase(...):從容器中刪除一個或幾個元素。該函數參數較復雜,此處省略。
- clear():從容器中刪除所有元素。
如果一個容器是空的,則 begin() 和 end() 的返回值相等,rbegin() 和 rend() 的返回值也相等。
順序容器還有以下常用成員函數:
- front():返回容器中第一個元素的引用。
- back():返回容器中最后一個元素的引用。
- push_back():在容器末尾增加新元素。
- pop_back():刪除容器末尾的元素。
- insert(...):插入一個或多個元素。該函數參數較復雜,此處省略。
?
迭代器(iterator)
?
要訪問順序容器和關聯容器中的元素,需要通過“迭代器(iterator)”進行。迭代器是一個變量,相當于容器和操縱容器的算法之間的中介。迭代器可以指向容器中的某個元素,通過迭代器就可以讀寫它指向的元素。從這一點上看,迭代器和指針類似
1) 正向迭代器,定義方法如下:容器類名::iterator? 迭代器名;
2) 反向迭代器,定義方法如下:容器類名::reverse_iterator? 迭代器名;
程序的輸出結果是:
0 1 2 3 4
8 6 4 2 0
第 6 行,vector 容器有多個構造函數,如果用無參構造函數初始化,則容器一開始是空的。
第 10 行,begin 成員函數返回指向容器中第一個元素的迭代器。++i 使得 i 指向容器中的下一個元素。end 成員函數返回的不是指向最后一個元素的迭代器,而是指向最后一個元素后面的位置的迭代器,因此循環的終止條件是i != v.end()。
第 16 行定義了反向迭代器用以遍歷容器。反向迭代器進行++操作后,會指向容器中的上一個元素。rbegin 成員函數返回指向容器中最后一個元素的迭代器,rend 成員函數返回指向容器中第一個元素前面的位置的迭代器,因此本循環實際上是從后往前遍歷整個數組。
如果迭代器指向了容器中最后一個元素的后面或第一個元素的前面,再通過該迭代器訪問元素,就有可能導致程序崩潰,這和訪問 NULL 或未初始化的指針指向的地方類似。
不同容器的迭代器的功能
1) 正向迭代器。假設 p 是一個正向迭代器,則 p 支持以下操作:++p,p++,*p。此外,兩個正向迭代器可以互相賦值,還可以用==和!=運算符進行比較。
2) 雙向迭代器。雙向迭代器具有正向迭代器的全部功能。除此之外,若 p 是一個雙向迭代器,則--p和p--都是有定義的。--p使得 p 朝和++p相反的方向移動。
3) 隨機訪問迭代器。隨機訪問迭代器具有雙向迭代器的全部功能。若 p 是一個隨機訪問迭代器,i 是一個整型變量或常量,則 p 還支持以下操作:
- p+=i:使得 p 往后移動 i 個元素。
- p-=i:使得 p 往前移動 i 個元素。
- p+i:返回 p 后面第 i 個元素的迭代器。
- p-i:返回 p 前面第 i 個元素的迭代器。
- p[i]:返回 p 后面第 i 個元素的引用。
此外,兩個隨機訪問迭代器 p1、p2 還可以用 <、>、<=、>= 運算符進行比較。p1<p2的含義是:p1 經過若干次(至少一次)++操作后,就會等于 p2。其他比較方式的含義與此類似。
對于兩個隨機訪問迭代器 p1、p2,表達式p2-p1也是有定義的,其返回值是 p2 所指向元素和 p1 所指向元素的序號之差(也可以說是 p2 和 p1 之間的元素個數減一)。
下面的程序中,每個循環演示了一種做法。
#include <iostream> #include <vector> using namespace std; int main() {vector<int> v(100); //v被初始化成有100個元素for(int i = 0;i < v.size() ; ++i) //size返回元素個數cout << v[i]; //像普通數組一樣使用vector容器vector<int>::iterator i;for(i = v.begin(); i != v.end (); ++i) //用 != 比較兩個迭代器cout << * i;for(i = v.begin(); i < v.end ();++i) //用 < 比較兩個迭代器cout << * i;i = v.begin();while(i < v.end()) { //間隔一個輸出cout << * i;i += 2; // 隨機訪問迭代器支持 "+= 整數" 的操作} }迭代器的輔助函數
STL 中有用于操作迭代器的三個函數模板,它們是:
- advance(p, n):使迭代器 p 向前或向后移動 n 個元素。
- distance(p, q):計算兩個迭代器之間的距離,即迭代器 p 經過多少次 + + 操作后和迭代器 q 相等。如果調用時 p 已經指向 q 的后面,則這個函數會陷入死循環。
- iter_swap(p, q):用于交換兩個迭代器 p、q 指向的值。
下面的程序演示了這三個函數模板的 用法。
#include <list> #include <iostream> #include <algorithm> //要使用操作迭代器的函數模板,需要包含此文件 using namespace std; int main() {int a[5] = { 1, 2, 3, 4, 5 };list <int> lst(a, a+5);list <int>::iterator p = lst.begin();advance(p, 2); //p向后移動兩個元素,指向3cout << "1)" << *p << endl; //輸出 1)3advance(p, -1); //p向前移動一個元素,指向2cout << "2)" << *p << endl; //輸出 2)2list<int>::iterator q = lst.end();q--; //q 指向 5cout << "3)" << distance(p, q) << endl; //輸出 3)3iter_swap(p, q); //交換 2 和 5cout << "4)";for (p = lst.begin(); p != lst.end(); ++p)cout << *p << " ";return 0; }程序的輸出結果是:
1) 3
2) 2
3) 3
4) 1 5 3 4 2
?
STL中“大”、“小”和“相等”的概念
stl中關聯容器內部的元素是排序的。STL 中的許多算法也涉及排序、查找。這些容器和算法都需要對元素進行比較。
默認情況下,比較大小是通過<運算符進行的,和>運算符無關。在STL中提到“大”、“小”的概念時,以下三個說法是等價的:
x 比 y 小。表達式x<y為真。y 比 x 大。
一定要注意,y比x大意味著x<y為真,而不是y>x為真。y>x的結果如何并不重要。
在 STL 中,x和y相等也往往不等價于x==y為真。對于在未排序的區間上進行的算法,如順序查找算法 find,查找過程中比較兩個元素是否相等用的是==運算符;但是對于在排好序的區間上進行查找、合并等操作的算法(如折半查找?binary_search,關聯容器自身函數 find)來說,x和y相等是與x<y和y<x同時為假等價的,與==運算符無關。
看上去x<y和y<x同時為假就應該和x==y為真等價,其實不然。例如下面的 class A:
class A{public:bool operator< (const A & a)const {return false;} };可以看到,對任意兩個類 A 的對象 x、y,x<y和y<x都是為假的。也就是說,對 STL 的關聯容器和許多算法來說,任意兩個類 A 的對象都是相等的,這與==運算符的行為無關。
綜上所述,使用 STL 中的關聯容器和許多算法時,往往需要對<運算符進行適當的重載,使得這些容器和算法可以用<運算符對所操作的元素進行比較。最好將<運算符重載為全局函數,因為在重載為成員函數時,在有些編譯器上會出錯(由其 STL 源代碼的寫法導致)。
總結
以上是生活随笔為你收集整理的C++ STL与迭代器的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: (图文详细)如何使用Code::Bloc
- 下一篇: PHP实现远程下载文件到本地