1. 程式人生 > >跟我學STL系列(1)——STL入門介紹

跟我學STL系列(1)——STL入門介紹

一、引言

最近這段時間一直都在自學C++,所以這裡總結下自己這段時間的學習過程,通過這種方式來鞏固自己學到的內容和以備後面複習所用,另外,希望這系列文章可以幫助到其他自學C++的朋友們。

由於本人之前主要研究C#語言,在自學C++的過程中,經常會把C++中內容與C#中內容進行對比來理解,所以這系列文章的內容也會與C#進行比較,從而來說明語言都是想通的,只要你掌握好一門語言,學習其他語言都可以舉一反三。

二、STL是什麼

STL全稱為Standard Template Library,即標準模板庫,該庫提供一些常用的容器物件和一些通用的演算法等,大家可以理解STL就是一個庫,該庫幫我們封裝了很多容器類和通用的方法,我們可以通過呼叫該庫中封裝好的方法和容器類來進行程式設計,相比C#而言,STL就好比.NET類庫中的某個DLL,例如,C# 中,List<T>類存在於mscorlib.dll中System.Collections.Generic名稱空間下,C++ 中,list<T>存在於list標頭檔案中std名稱空間下,所以C++程式碼中要使用list<T>容器(在C++中把list成為容器,即一系列元素的集合),必須先通過#include <list>引入list標頭檔案,類似與C#中的新增mscorlib.dll引言,再使用use namespace std引入名稱空間,類似與C#中using System.Collections.Generic程式碼。

三、STL 六大元件

STL通過模板抽象了基於資料結構之上的普遍行為,形成了獨特的STL演算法。在STL中,這些資料結構成為容器。在容器和演算法之間通過中間體:迭代器來進行連線,迭代器可以看做是資料結構和演算法之間的紐帶,它降低了資料結構和演算法之間的耦合度。STL中國又包括六大核心元件,它們分別是:

  • 容器(Container)
  • 演算法(Algorithm)
  • 迭代器(Iterator)
  • 函式物件,又稱仿函式(Function object)
  • 介面卡(Adaptor)
  • 空間配置器(Allocator)
下面對這六大元件分別進行介紹。

3.1 容器

STL中容器可分為序列式容器和關聯式容器,其中,序列式容器的每個元素的位置取決於元素被插入時設定的位置,和元素值本身無關,而,關聯式容器的元素位置取決於特定的排序規則,和插入順序無關。意思就說,序列式容器中每個元素的位置與插入的順序對應,而關聯式容器中元素會根據特定的排序規則對每個元素進行排序,與元素插入的順序無關,更多內容可以參考本系列後面文章介紹。

序列式容器包括:vector、list和deque,而關聯式容器有:set、multiset、map和multimap。下表列出了每個容器的簡單介紹:
容器 特徵 記憶體結構 可隨機存取 元素搜尋速度 標頭檔案
vector 在序列尾部進行插入和刪除,訪問和修改元素的時間複雜度為O(1),
但插入和刪除的時間複雜度與到末尾的距離成正比。
單端陣列 可以 <vector>
list 對任意元素的訪問與兩端的距離成正比,但對某個位置的插入和刪除
花費為常數時間,即O(1)
雙向連結串列 非常慢 <list>
deque 與vector基本相同,唯一不同的是,在序列頭部插入和刪除的
時間複雜度也是O(1)
雙端陣列 可以  <deque>
set 由節點組成的紅黑樹,具有快速查詢的功能 二叉樹 <set>
multiset 可以支援重複元素,同樣具有快速查詢能力 二叉樹 <set>
map 由{鍵,值}對組成的集合,同樣具有快速查詢能力 二叉樹 對key而言可以 對key而言快 <map>
multimap 一個鍵可以對應於多個值,同樣具有快速查詢能力 二叉樹 對key而言快 <map>

上面列出的所有容器在C#中都有相應的對應形式,它們之間的對應關係為: std::vector——List<T> std::set——HashSet<T>類,但這裡需要明確,STL中的set是以紅黑樹作為底層資料結構,而C#中HashSet<T>類是以雜湊表作為底層資料結構,因為其兩者使用資料結構的不同,從而導致查詢效率不同,set查詢的花費時間為O(logn),這也是紅黑樹查詢時間,而HashSet的查詢花費時間為O(1)。 std::multimap——Dictionary<TKey,List<TValue>>,該類在C#中也不存在的,也需要自己實現 std::multiset——Dictionary<TKey,int>(第二個引數儲存著Key的數量) C++中的std::deque在C#中並沒有找到相對應地實現,不過我們可以自己實現,具體實現可以參考文章:A Deque Class in C# 在上面的對應關係中,C#中的SortedDictionary<TKey,TValue>類是以二叉查詢樹作為底層資料結構的,而Dictionary<TKey,TValue>類是以雜湊表作為底層資料結構的。因為其資料結構的不同從而導致操作效率的不同,下表列出了兩者各種操作的區別。
操作 Dictionary<Key,Value> SortedDictionary<Key,Value>
this[key] O(1) O(logn)
Add(key,value) O(1)  O(logn)
Remove(key) O(1) O(logn)
ContainsKey(key) O(1) O(logn)
ContainsValue(value) O(1) O(n)

3.2 演算法

演算法是用來操作容器中資料的模板函式,它抽象了對資料結構的操作行為。要使用STL中定義的演算法,應首先引入<algorithm>標頭檔案。例如STL中的sort()函式可以對容器中的資料進行排序,可以使用find()函式來搜尋容器中的某個元素。這裡的演算法可以與C#中泛型方法進行對比來理解。

3.3 迭代器

STL實現要點是將容器和演算法分開,使兩者彼此獨立。迭代器使兩個聯絡起來,迭代器提供訪問容器中的方法。迭代器實質上是一種智慧指標,它過載了->和*操作符。事實上,C++指標也是一種迭代器。在C#中同樣有迭代器的概念,具體參考MSDN:http://msdn.microsoft.com/zh-cn/library/dscyy5s0(v=vs.90).aspx,不同的是,在C++ 中迭代器分為五類,這五類分別為:

  1. 輸入迭代器(Input Iterator)——提供對資料的只讀訪問;
  2. 輸出迭代器(Output Iterator)——提供對資料的只寫訪問;
  3. 前推迭代器(Forward Iterator)——提供對資料的讀寫操作,並能向前推進的迭代器;
  4. 雙向迭代器(Bidirectional Iterator)——提供對資料的讀寫操作,並能向前和向後操作;
  5. 隨機訪問迭代器(Random Access Iterator)——提供對資料的讀寫操作,並能在資料中隨機移動。

3.4 函式物件

函式物件,又稱為仿函式,STL中的函式物件就是過載了運算子()的模板類的物件,因為該類物件的呼叫方式類似與函式的呼叫方式,所以稱為函式物件,函式物件類似於C#中的委託物件,熟悉C#的朋友肯定知道,我們可以隱式地呼叫委託,即委託物件(實參),更多關於委託內容可以參考我的博文:委託的本質論

3.5 介面卡

介面卡是用來修改其他元件介面,與設計模式中的介面卡模的達到的效果是一樣的。STL中定義了3種形式的介面卡:容器介面卡、迭代器適配和函式介面卡。

容器介面卡——包括棧(stack)、佇列(queue)和優先佇列(priority_queue),容器介面卡是對基本容器型別進行進一步的封裝,從而轉換為新的介面型別。

迭代器介面卡——對STL中基本迭代器的功能進行擴充套件,該類介面卡包括反向迭代器、插入迭代器和流迭代器。

函式介面卡——通過轉換或修改來擴充套件其他函式物件的功能。該類介面卡有否定器、繫結器和函式指標介面卡。函式物件介面卡的作用就是使函式轉化為函式物件,或將多引數的函式物件轉換為少引數的函式物件,如STL中bind2nd()就是繫結器。

3.6 空間配置器

當容器中儲存的是使用者自定義型別資料時,有的資料型別結構簡單,佔用的空間很小,而有的資料型別結構複雜,佔用的記憶體空間較大;並且有的應用程式需要頻繁地進行資料的插入刪除操作,這樣就需要對記憶體空間進行頻繁地申請和釋放工作,然而對記憶體的頻繁操作,會產生嚴重的效能問題,為了解決這個問題,STL中提供了兩個空間配置器,一個是簡單空間配置器,僅僅對C執行庫中malloc和free進行了簡單的封裝操作,另一個是“基於記憶體池的控制元件配置器”,即容器在每次申請記憶體的時候,記憶體池會基於一定的策略,向作業系統申請交大的記憶體空間,從而避免每次都向OS申請記憶體。STL中的空間配置器就是負責記憶體的分配和釋放的工作。

四、STL中容器使用示例

看完STL元件之後,現在我們具體看看如何使用STL中的容器進行程式設計,下面示例是對vector容器進行簡單的幾個操作。具體程式碼如下:

#include <iostream>
// 要使用vector容器必須加入vector標頭檔案
#include <vector>
// 引入演算法標頭檔案
#include <algorithm>
// 引入std名稱空間,如果不引入名稱空間,則必須像std::vector這樣方式來使用vector容器
using namespace std;

void main()
{
	// 初始化vector容器物件
	vector<int> vec;
	for(int i=0;i<10;i++)
	{
		// 向vector中新增一個元素
		vec.push_back(i);
	}

	// 使用陣列初始化vector容器
	int a[]={3,2,1,0,9,5};
	vector<int> vec2(a,a+6); // 使用a陣列第一位到第六位來初始化vector容器

	// 定義迭代器物件
	vector<int>::iterator begin;

	// 遍歷vector集合
	for(begin =vec.begin();begin!=vec.end();begin++)
	{
		// 輸出vector容器中的元素
		cout<<*begin<<"  ";
	}
	cout<<endl;

	// 對vec2容器排序
	sort(vec2.begin(),vec2.end());
	// 遍歷vector集合
	cout<<"對vec2容器排序後的結果:"<<endl;
	for(begin =vec2.begin();begin!=vec2.end();begin++)
	{
		// 輸出排序後vec2容器中的元素	
		cout<<*begin<<"  ";
	}
	cout<<endl;
}
上面程式碼的輸出結果如下圖所示:


五、小結

該篇博文只是對STL進行一個全面的介紹,希望初學者對STL能有一個全面的認識,並且本文對於一些難懂的內容,都用C#中相關的內容進行了解釋,如果有像我一樣,之前學習C#朋友可以對比來進行理解,關於STL六大元件的詳細介紹會在後面文章進行介紹。 PS:這裡需要說明的一點,本人開始學習C++,並不是因為C#語言不行,用大牛的一句說就是:語言只是工具,用來實現你思考的工具,重要在於思想和遇到問題的解決方式,並且希望通過C++的學習可以對一些底層的東西可以有一個更深入的認識,因為C#封裝的太好,確實我們要去了解底層會有點難,但是並不說不可以,其實它們之間說白了也是差不多的,只是C++可以更好地去了解底層的一些操作罷了,並且我學習的過程,時刻與C#進行對比來進行理解C++的內容,相信這也是一個對C#的鞏固的過程。