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? 迭代器名;

#include <iostream>
#include <vector>
using namespace std;
int main()
{vector<int> v;  //v是存放int類型變量的可變長數組,開始時沒有元素for (int n = 0; n<5; ++n)v.push_back(n);  //push_back成員函數在vector容器尾部添加一個元素vector<int>::iterator i;  //定義正向迭代器for (i = v.begin(); i != v.end(); ++i) {  //用迭代器遍歷容器cout << *i << " ";  //*i 就是迭代器i指向的元素*i *= 2;  //每個元素變為原來的2倍}cout << endl;//用反向迭代器遍歷容器for (vector<int>::reverse_iterator j = v.rbegin(); j != v.rend(); ++j)cout << *j << " ";return 0;
}

程序的輸出結果是:
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 是一個雙向迭代器,則--pp--都是有定義的。--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<yy<x都是為假的。也就是說,對 STL 的關聯容器和許多算法來說,任意兩個類 A 的對象都是相等的,這與==運算符的行為無關。

綜上所述,使用 STL 中的關聯容器和許多算法時,往往需要對<運算符進行適當的重載,使得這些容器和算法可以用<運算符對所操作的元素進行比較。最好將<運算符重載為全局函數,因為在重載為成員函數時,在有些編譯器上會出錯(由其 STL 源代碼的寫法導致)。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/444068.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/444068.shtml
英文地址,請注明出處:http://en.pswp.cn/news/444068.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

在JSP頁面中輸出JSON格式數據

JSON-taglib是一套使在JSP頁面中輸出JSON格式數據的標簽庫。 JSON-taglib主頁&#xff1a; http://json-taglib.sourceforge.net/index.html JAR包下載地址&#xff1a; http://sourceforge.net/projects/json-taglib/files/latest/download 使用方法&#xff1a; 1、下載js…

git/github使用完整教程(1)基礎

安裝git 在Linux上安裝Git 首先輸入git&#xff0c;看看系統有沒有安裝Git&#xff1a; $ git The program git is currently not installed. You can install it by typing: sudo apt-get install git像上面的命令&#xff0c;有很多Linux會友好地告訴你Git沒有安裝&#x…

git/github使用完整教程(2)分支

分支 首先&#xff0c;我們創建dev分支&#xff0c;然后切換到dev分支&#xff1a; $ git checkout -b dev Switched to a new branch devgit checkout命令加上-b參數表示創建并切換&#xff0c;相當于以下兩條命令&#xff1a; $ git branch dev $ git checkout dev Switch…

數組【數據結構】

前提 數組的定義以及數組的延伸 這種不好進行理解&#xff0c;那么我們下面以二維數組進行解釋 多維數組的數據特點 存儲數組結構的兩種方式 問題抽象總結

Kafka深度解析

原創文章&#xff0c;轉載請務必將下面這段話置于文章開頭處&#xff08;保留超鏈接&#xff09;。 本文轉發自技術世界&#xff0c;原文鏈接 http://www.jasongj.com/2015/01/02/Kafka深度解析 背景介紹 Kafka簡介 Kafka是一種分布式的&#xff0c;基于發布/訂閱的消息系統。…

數據的存儲特殊矩陣壓縮存儲【數據結構F】

以行為主序 以列為主序 矩陣的前提分類 三角矩陣

C++ 多態和虛函數

虛函數實現多態 #include <iostream> using namespace std;//基類People class People{ public:virtual void display(); //聲明為虛函數 }; void People::display(){cout<<"無業游民。"<<endl; }//派生類Teacher class Teacher: public People{…

圖的基本概念【數據結構】

序言 1對1的線性結構&#xff0c;一對多的樹二叉樹以及森林&#xff0c;第3種就是多對多的結構&#xff0c;也就是我們所要講到的圖的結構&#xff0c;圖形結構是數據結構當中最復雜的一種結構&#xff0c;圖形結構的特點就是在這個圖當中任意兩點之間都會有關系&#xff0c;這…

go語言一天入門(上)

第一個go程序 package mainimport "fmt"func main() {/* 這是我的第一個簡單的程序 */fmt.Println("Hello, World!") } 第一行代碼 package main 定義了包名。你必須在源文件中非注釋的第一行指明這個文件屬于哪個包&#xff0c;如&#xff1a;package m…

Jquery 全選,反選

<script src"../js/jquery-1.6.2.min.js" type"text/javascript"></script><script language"javascript" type"text/javascript">$(function(){$("#selectAll").click(function(){//全選$("#playLi…

go語言一天入門(下)

結構體 和c一樣 package mainimport "fmt"type Books struct {title stringauthor stringsubject stringbook_id int }func main() {// 創建一個新的結構體fmt.Println(Books{"Go 語言", "www.runoob.com", "Go 語言教程", 6495407}…

圖的遍歷算法【數據結構F】

圖的遍歷算法有哪兩種&#xff1f; 深度優先調度算法---------將圖結構看成是樹形結構&#xff0c;樹形結構的子圖直接是沒有交叉的&#xff0c;但是對于圖結構的樹形結構之間是有交叉的&#xff0c;類比于樹形結構的二叉樹&#xff0c;左指數和右指數都會相應的經歷三次&#…

go語言快速刷《程序員面試金典》(1)

實現一個算法&#xff0c;確定一個字符串 s 的所有字符是否全都不同。 一個數組統計是否有 func isUnique(astr string) bool {var arr[26] int;for _,ch:range astr{num:ch-aif(arr[num]1){return false}arr[num]}return true } 給定兩個字符串 s1 和 s2&#xff0c;請編寫一…

最小生成樹【數據結構】

前提 【1】網的最小生成樹&#xff0c;涉及到生成樹了那么就會有最小的權值在里面了 【2】對于一個圖來說生成樹是由多個的&#xff0c;并不是唯一的 【3】&#xff1a;廣度優先算法的遍歷是可以得到生成樹的&#xff0c;深度優先算法也是可以得到生成樹的 任意的一個聯通網&am…

go語言快速刷《程序員面試金典》(2)

字符串輪轉。給定兩個字符串s1和s2&#xff0c;請編寫代碼檢查s2是否為s1旋轉而成&#xff08;比如&#xff0c;waterbottle是erbottlewat旋轉后的字符串&#xff09;。 示例1 輸入&#xff1a;s1 "waterbottle", s2 "erbottlewat" 輸出&#xff1a;T…

廣義表的基本概念【數據結構】

實名廣義表與匿名廣義表的區別&#xff1a;對于匿名的廣義表的表示方法我們認為一對括號就是一個廣義表&#xff0c;里面的數據可以是廣義表也可以是 原子&#xff0c;對于有名字的廣義表&#xff0c;也就是大寫的字母我們可以直接認為大寫的就是廣義表的表示方法小練習----廣義…

go語言快速刷《程序員面試金典》(3)

編寫程序以 x 為基準分割鏈表&#xff0c;使得所有小于 x 的節點排在大于或等于 x 的節點之前。如果鏈表中包含 x&#xff0c;x 只需出現在小于 x 的元素之后(如下所示)。分割元素 x 只需處于“右半部分”即可&#xff0c;其不需要被置于左右兩部分之間。 示例: 輸入: head …

樹和二叉樹【數據結構】

基本概念 ADT的定義 基本操作 對比樹形結構和線性結構 基本術語以及注意事項-不能錯誤簡單的我以為 二叉樹是度數小于等于2的樹&#xff0c;而不是度為2的樹&#xff0c;一定要記住這個概念 小知識&#xff1a;二進制轉換成為十進制的方法名稱叫做位權求和法&#xff0c;用到…