1. 程式人生 > >C++進階與拔高(四)(C++ STL迭代器)

C++進階與拔高(四)(C++ STL迭代器)

第二章 C++ STL

        本章節所介紹的C++ STL主要基於三個內容:迭代、演算法和容器。所參考的資料均來自於CSDN部落格以及P.J.PLAUGER所著的《C++ STL中文版》。主要的參考文獻將在本書最後給出。

2.1 C++ STL簡介

       STL(Standard Template Library)標準模板庫是惠普實驗室開發的一系列軟體的統稱。標準模板庫,是一個具有工業強度的,高效的C++程式庫。它被容納於C++標準程式庫(C++ Standard Library)中,是ANSI/ISO C++標準中最新的也是極具革命性的一部分。該庫包含了諸多在電腦科學領域裡所常用的基本資料結構和基本演算法。為廣大C++程式設計師們提供了一個可擴充套件的應用框架,高度體現了軟體的可複用性。

       STL程式碼從廣義上大致分為3類:演算法algorithm,容器container和迭代器iterator。演算法是決定結果的一個數學過程。STL容器是一些模板類,迭代器用於聯絡演算法和容器。幾乎STL所提供的所有演算法都是通過迭代器儲存元素序列進行工作的。STL中的每一個容器都定義了其本身所專有的迭代器,用以存取容器中的元素。

container容器

algorithm演算法

iterator演算法

陣列、堆疊、佇列、連結串列或二叉樹、紅黑樹

排序、增刪、改查等

訪問容器的一種類似於指標的工具

2.1.1 功能描述

       組成C++中STL部分的標頭檔案由以下13個組成,可以使用include指令將標準標頭檔案中的內容包含到程式中,如下。

algorithm

deque

functional

iterator

list

map

memory

numeric

queue

set

stack

utility

vector

#include<algorithm>//include all algorithms

       STL的一個重要特點是資料結構和演算法的分離。儘管這是個簡單的概念,但這種分離確實使得STL變得非常通用。例如,由於STL的sort()函式是完全通用的,你可以用它來操作幾乎任何資料集合,包括連結串列,容器和陣列;

       STL另一個重要特性是它不是面向物件的。為了具有足夠通用性,STL主要依賴於模板而不是封裝,繼承和虛擬函式(多型性)——OOP的三個要素。你在STL中找不到任何明顯的類繼承關係。這好像是一種倒退,但這正好是使得STL的元件具有廣泛通用性的底層特徵。另外,由於STL是基於模板,行內函數的使用使得生成的程式碼短小高效;

2.1.2 使用STL

一、名稱空間

       using namespace std;

       這樣做會使所有在庫中定義的名字都暴露在當前的名稱空間中,於是就不需要在這些名字前新增字首std::。如果想把庫中定義的所有名字都暴露在全域性名稱空間,則需要在每個原始檔的開始處都寫上這句宣告。

二、標頭檔案

       標頭檔案通常被用於解釋的註釋以及用於防範措施的守衛巨集所包圍。如utility檔案:

Eg:

//utility standard header

#ifndef _UTILITY_

#define _UTILITY_

……

#endif /* _UTILITY_ */

       這種方法可以防止一個頭檔案被程式包含多次。

2.2 STL迭代器

       概括來說,迭代器在STL中用來將演算法和容器聯絡起來,起著一種黏和劑的作用。迭代器部分主要由<utility>,<iterator>和<memory>三個標頭檔案為基礎展開討論。

       <utility>是一個很小的標頭檔案,它包括了貫穿使用在STL中的幾個模板的宣告。

       <iterator>中提供了迭代器使用的許多方法,而對於<memory>的描述則十分的困難,它以不同尋常的方式為容器中的元素分配儲存空間,同時也為某些演算法執行期間產生的臨時物件提供機制,<memory>中的主要部分是模板類allocator,它負責產生所有容器中的預設分配器。

2.2.1 功能描述

       C++中的迭代器相對於C中的物件指標來說更加一般化,指標本身就可以作為定義好的迭代器來使用。

一、輸出迭代器

       可使用迭代器來存取有序序列中的元素,“存取”既可以表示將值儲存到物件中,也可以表示從一個物件中獲得它所儲存的值。

例 2.2.1 建立新的序列,並以有序方式產生值。

for (;<not done>;++next)
//next表示一個迭代器型別為X的物件,<not done>用於檢測終止條件
*next=<whatever>;//<whatever>是一個表示式

輸出迭代器的屬性

表示式

結果的型別

含義

註釋

X(a)

X

產生一個a的拷貝

*X(a)=t與*a=t作用相同

X u(a)

X u=a

X&

u是a的拷貝

X是迭代器型別;a的型別為X&,T是元素型別,t的型別為T。

       一個輸出迭代器至少需要定義以下操作:

  • *next=<whatever>:將<whatever>的值賦給序列中將要產生的下一個元素。
  • ++next改變next的值,使其指向序列中的下一個元素。

二、輸入迭代器

       輸入迭代器用於產生新的序列。用於順序存取已有的值或對已有的序列進行遍歷。

Eg:

for (p=first;p!=last;++p)//p,first和last都是迭代器型別X的物件
<process>(*p);//<process>是一個函式,能夠接受元素型別為T的引數
//其中first和last各代表一個迭代器

       輸入迭代器至少需要定義以下操作:

  • 當兩個型別為X的迭代器p和q沒有指向同一個元素時,p!=q就為真。
  • *p是型別T的一個右值。
  • ++p改變p的值,將它指向序列中目前所指向元素的下一個元素。

輸入迭代器的屬性

表示式

結果的型別

含義

註釋

X(a)

X

產生一個a的拷貝

*X(a)與*a作用相同

X u(a)

X u=a

X&

u是a的拷貝

建立完成後u==a

X是迭代器型別;a的型別為X&,T是元素型別,t的型別為T。

三、end of sequence值

       該值由輸入迭代器定義,它在大多數情況下是一種end of file標記,end of sequence值通常儲存在last中,用於記錄迭代器first是否等於last。即我們可以通過增加輸入迭代器的值來遍歷一個檔案。

四、前向迭代器

       輸入/輸出迭代器可以處理任意長度的檔案,但是迭代器的更加普遍的用法是:存取一個完全儲存在記憶體中的序列。使用前向迭代器可是實現序列中元素的同時讀和寫,也可以實現“書籤”操作。在這些情況下,迭代器更像一個傳統的指標。

       可以把前向迭代器想象成為一個指向單鏈表中元素的指標,如果它指向的不是連結串列的末端,則就可以通過它來存取連結串列中的元素,或是把它移到序列中下一個元素的位置。但是不能通過它來直接存取連結串列中的任意元素。

五、雙向迭代器

       這一種迭代器可以同時支援遞增遞減操作。可以將其想象為指向一個雙向連結串列中元素的指標。用null來標記end of sequence。若迭代器指向的不是連結串列的末端,則可以通過它來存取連結串列中的元素,或者向下移位。若迭代器指向的不是連結串列中第一個元素,則可以把它回退到序列中前一個元素的位置。

六、隨機存取迭代器

       不再贅述。

2.2.2 功能描述

       STL中大量使用了迭代器,它們用於不同的演算法和演算法所做用的序列之間,起著橋樑的作用。下面總結了一些常用的迭代器。

OutIt輸出迭代器

若X為一個輸出迭代器,則它只能通過儲存來間接地擁有一個值V,且在儲存值之後,需要遞增。

(*X++=V)或者(*X=V,X++)

InIt輸入迭代器

若X為一個輸入迭代器,若它的值不等於end of sequence的話,就可以通過間接的方式來存取它所擁有的值V。

(V=*x)

若想取得序列中下一個元素的值,則需將其遞增。如++X,或*V=X++

FwdIt前向迭代器

若X為一個前向迭代器,如果*X是可變的,那麼它就可以替換輸出迭代器,同理也可以替換輸入迭代器。

BidIt雙向迭代器

若X為一個雙向迭代器,則其可以替換前向迭代器。

RanIt隨機存取迭代器

迭代器的種類:

只寫操作(write only)

只讀操作(read only)

讀寫操作(read/write)

輸出迭代器可以被替換為:

前向迭代器

雙向迭代器

隨機存取迭代器

輸入迭代器可以被替換為:

前向迭代器

雙向迭代器

隨機存取迭代器

前向迭代器可以被替換為:

雙向迭代器

隨機存取迭代器

物件指標總是可以當作隨機存取迭代器來使用