C++ STL常用容器淺析


 

首先要理解什么是容器,在C++中容器被定義為:在數據存儲上,有一種對象類型,它可以持有其它對象或指向其它對象的指針,這種對象類型就叫做容器。簡單來說

容器就是包含其他類的對象們的對象,當然這種(容器)對象中還包含了一系列處理其所包含對象以及要包含對象的處理。

 

其次介紹一下STL,它由容器算法迭代器組成;

STL可以實現方便容易的搜索數據或對數據排序等一系列的算法。

 

STL 對定義的通用容器分三類:順序性容器關聯式容器容器適配器

 

順序容器:一種各元素之間有順序關系的線性表,是一種線性結構的可序群集。順序性容器中的每個元素均有固定的位置,除非用刪除或插入的操作改變這個位置。

這個位置和 元素本身無關,而和操作的時間和地點有關,順序性容器不會根據元素的特點排序而是直接保存了元素操作時的邏輯順序。比如我們一次性對一個順序

性容器追加三 個元素,這三個元素在容器中的相對位置和追加時的邏輯次序是一致的。

關聯容器:他和順序容器不同關聯容器是一種非線型的二叉樹結構,各元素之間沒有嚴格的物理順序關系,也就是說元素在容器中並沒有保存元素置 入容器先后的的邏輯順序。但是關聯式容器提供了另一種根據元素特點排序的功能,這樣迭代器就能根據元素的特點“順序地”獲取元素。

順序容器中常用的主要有vector,list,deque等。

容器適配器 :是一個比較抽象的概念, C++的 解釋是:適配器是使一事物的行為類似於另一事物的行為的一種機制。容器適配器是讓一種已存在的容器類型采用另一種不同的抽象類型的工作方式來實現的一種機 制。其實僅是發生了接口轉換。那么你可以把它理解為容器的容器,它實質還是一個容器,只是他不依賴於具體的標准容器類型,可以理解是容器的模版。或者把它 理解為容器的接口,而適配器具體采用哪種容器類型去實現,在定義適配器的時候可以由你決定。

 

為了定義一個容器類型的對象必須包含相關的頭文件:

1 定義vector:  #include <vector>
2 定義list:    #include <list>
3 定義deque:   #include <deque>
4 定義queue:   #include <queue>
5 定義stack:    #include <stack>  //注意:stack和queue都是基於deque實現的,但是stack和queue比較常用
頭文件包含

 

 

1 vector<int>vec;//定義vector,常用
2 list<int>lis;
3 deque<int>deq;
4 stack<int>sta;//定義棧,常用
5 queue<int>que;//定義棧,常用
定義示例

 

 

 

 

順序容器中vector最為常用,它表示一段連續的內存,基於數組實現:

1     vector<int> vec1;    //默認初始化,vec1為空
2 
3     vector<int> vec2(vec1);  //使用vec1初始化vec2
4 
5     vector<int> vec3(vec1.begin(),vec1.end());//使用vec1初始化vec2
6 
7     vector<int> vec4(10);    //10個值為的元素
8 
9     vector<int> vec5(10,4);  //10個值為的元素
Vector的定義和初始化

 

 1 //vector的定義
 2 vector                   創建一個空的vector。
 3 vector  c1(c2)           復制一個vector
 4 vector  c(n)             創建一個vector,含有n個數據,數據均已缺省構造產生
 5 vector  c(n, elem)      創建一個含有n個elem拷貝的vector
 6 vector  c(beg,end)       創建一個含有n個elem拷貝的vector
 7 
 8 //vector的操作
 9 c.assign(beg,end)        將[beg; end)區間中的數據賦值給c。
10 c.assign(n,elem)      將n個elem的拷貝賦值給c。 
11 c.at(idx)          傳回索引idx所指的數據,如果idx越界,拋出out_of_range。
12 c.back()                 傳回最后一個數據,不檢查這個數據是否存在。
13 c.begin()           傳回迭代器中的第一個數據地址。
14 c.capacity()         返回容器中數據個數。
15 c.clear()            移除容器中所有數據。
16 c.empty()            判斷容器是否為空。
17 c.end()              指向迭代器中末端元素的下一個,指向一個不存在元素。
18 c.erase(pos)         刪除pos位置的數據,傳回下一個數據的位置。
19 c.erase(beg,end)       刪除[beg,end)區間的數據,傳回下一個數據的位置。
20 c.front()            傳回第一個數據。
21 get_allocator        使用構造函數返回一個拷貝。
22 c.insert(pos,elem)      在pos位置插入一個elem拷貝,傳回新數據位置。
23 c.max_size()            返回容器中最大數據的數量。
24 c.insert(pos,n,elem)    在pos位置插入n個elem數據。無返回值。
25 c.insert(pos,beg,end)   在pos位置插入在[beg,end)區間的數據。無返回值。 
26 c.pop_back()           刪除最后一個數據。
27 c.push_back(elem)       在尾部加入一個數據。
28 c.rbegin()             傳回一個逆向隊列的第一個數據。
29 c.rend()               傳回一個逆向隊列的最后一個數據的下一個位置。
30 c.resize(num)          重新指定隊列的長度。
31 c.reserve()            保留適當的容量。
32 c.size()                返回容器中實際數據的個數。
33 c1.swap(c2)
34 swap(c1,c2)           將c1和c2元素互換。同上操作。
35 operator[]            返回容器中指定位置的一個引用。
vector容器的操作

 

 1 //下標法
 2 
 3     int length = vec1.size();
 4 
 5     for(int i=0;i<length;i++)
 6 
 7     {
 8 
 9        cout<<vec1[i];
10 
11     }
12 
13     cout<<endl<<endl;
14 
15     //迭代器法
16 
17     vector<int>::const_iterator iterator = vec1.begin();
18 
19     for(;iterator != vec1.end();iterator++)
20 
21     {
22 
23        cout<<*iterator;
24 
25     }
Vector容器的兩種遍歷方法

 

 vector 在需要的時候會擴容,在 VS 下是 1.5倍,在 GCC 下是 2 倍。

因為如果采用成倍方式擴容,可以保證常數的時間復雜度,而增加指定大小的容量只能達到O(n)的時間復雜度,因此,使用成倍的方式擴容。

顯然,增長的倍數不可能很大,也不會比 1 小,那么,它的最佳上限是多少呢?如果以 大於2 倍的方式擴容,下一次申請的內存會大於之前

分配內存的總和,導致之前分配的內存不能再被使用。所以,最好的增長因子在 (1,2)之間。

 

vector 的優點:
(1) 指定一塊如同數組一樣的連續存儲,但空間可以動態擴展。即它可以像數組一樣操作,並且可以進行動態操作。通常體現在push_back() pop_back() 。
(2) 隨機訪問方便,它像數組一樣被訪問,即重載了[ ] 操作符和vector.at()
(3) 節省空間,因為它是連續存儲,在存儲數據的區域都是沒有被浪費的,但是要明確一點vector 大多情況下並不是滿存的,在未存儲的區域實際是浪費的。

 

vector的缺點:

(4) 在內部進行插入、刪除操作效率非常低,這樣的操作基本上是被禁止的。Vector 被設計成只能在后端進行追加和刪除操作,其原因是vector 內部的實現是按照順序表的原理。

(5) 只能在vector 的最后進行push 和pop ,不能在vector 的頭進行push 和pop 。

(6) 當動態添加的數據超過vector 默認分配的大小時要進行內存的重新分配、拷貝與釋放,這個操作非常消耗性能。 所以要vector 達到最優的性能,最好在創建vector 時就指定其空間大小。

 

Vector內部數據結構:數組。
隨機訪問每個元素,所需要的時間為常量。
在末尾增加或刪除元素所需時間與元素數目無關,在中間或開頭增加或刪除元素所需時間隨元素數目呈線性變化。
可動態增加或減少元素,內存管理自動完成,但程序員可以使用reserve()成員函數來管理內存。
vector的迭代器在內存重新分配時將失效(它所指向的元素在該操作的前后不再相同)。當把超過capacity()-size()個元素插入vector中時,內存會重新分配,所有的迭代器都將失效;否則,指向當前元素以后的任何元素的迭代器都將失效。當刪除元素時,指向被刪除元素以后的任何元素的迭代器都將失效

 

 

關聯容器的特點是明顯的,相對於順序容器,有以下幾個主要特點:

1, 其內部實現是采用非線性的二叉樹結構,具體的說是紅黑樹的結構原理實現的;

2, set 和map 保證了元素的唯一性,mulset 和mulmap 擴展了這一屬性,可以允許元素不唯一;

3, 元素是有序的集合,默認在插入的時候按升序排列。

 

關聯容器中map最為常用,它提供一個鍵值對(key/value)容器:

1, 關聯容器對元素的插入和刪除操作比vector 要快,因為vector 是順序存儲,而關聯容器是鏈式存儲;比list 要慢,是因為即使它們同是鏈式結構,但list 是線性的,而關聯容器是二叉樹結構,其改變一個元素涉及到其它元素的變動比list 要多,並且它是排序的,每次插入和刪除都需要對元素重新排序;

2, 關聯容器對元素的檢索操作比vector 慢,但是比list 要快很多。vector 是順序的連續存儲,當然是比不上的,但相對鏈式的list 要快很多是因為list 是逐個搜索,它搜索的時間是跟容器的大小成正比,而關聯容器 查找的復雜度基本是Log(N) ,比如如果有1000 個記錄,最多查找10 次,1,000,000 個記錄,最多查找20 次。容器越大,關聯容器相對list 的優越性就越能體現;

3, 在使用上set 區別於vector,deque,list 的最大特點就是set 是內部排序的,這在查詢上雖然遜色於vector ,但是卻大大的強於list 。

4, 在使用上map 的功能是不可取代的,它保存了“鍵- 值”關系的數據,而這種鍵值關系采用了類數組的方式。數組是用數字類型的下標來索引元素的位置,而map 是用字符型關鍵字來索引元素的位置。在使用上map 也提供了一種類數組操作的方式,即它可以通過下標來檢索數據,這是其他容器做不到的,當然也包括set 。(STL 中只有vector 和map 可以通過類數組的方式操作元素,即如同ele[1] 方式)。

5.如果迭代器所指向的元素被刪除,則該迭代器失效。其它任何增加、刪除元素的操作都不會使迭代器失效。

 

 1 //1.定義和初始化
 2 
 3     map<int,string> map1;                  //空map
 4 
 5    
 6 
 7     //2.常用操作方法
 8 
 9     map1[3] = "Saniya";                    //添加元素
10 
11     map1.insert(map<int,string>::value_type(2,"Diyabi"));//插入元素
12 
13     //map1.insert(pair<int,string>(1,"Siqinsini"));
14 
15     map1.insert(make_pair<int,string>(4,"V5"));
16 
17     string str = map1[3];                  //根據key取得value,key不能修改
18 
19     map<int,string>::iterator iter_map = map1.begin();//取得迭代器首地址
20 
21     int key = iter_map->first;             //取得eky
22 
23     string value = iter_map->second;       //取得value
24 
25     map1.erase(iter_map);                  //刪除迭代器數據
26 
27     map1.erase(3);                         //根據key刪除value
28 
29     map1.size();                       //元素個數
30 
31     map1.empty();                       //判斷空
32 
33     map1.clear();                      //清空所有元素
map的定義與初始化

 

 1 /3.遍歷
 2 
 3     for(map<int,string>::iterator iter = map1.begin();iter!=map1.end();iter++)
 4 
 5     {
 6 
 7        int keyk = iter->first;
 8 
 9        string valuev = iter->second;
10 
11     }
Map的遍歷

 

在開發過程中我比較常用的就是這兩種容器,今天把他們從底層了解了一下,對我今后對容器的選擇和使用有了一定的幫助,吧今天了解到的整理下來希望可以用到。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM