C++標准模板庫(STL)和容器


1、什么是標准模板庫(STL)?

(1)C++標准模板庫與C++標准庫的關系

C++標准模板庫其實屬於C++標准庫的一部分,C++標准模板庫主要是定義了標准模板的定義與聲明,而這些模板主要都是

類模板,我們可以調用這些模板來定義一個具體的類;與之前的自己手動創建一個函數模版或者是類模板不一樣,我們使用了

STL就不用自己來創建模板了,這些模板都定義在標准模板庫中,我們只需要學會怎么使用這些類模板來定義一個具體的類,

然后能夠使用類提供的各種方法來處理數據。

(2)STL六大組件:容器(containers)、算法(algorithms)、迭代器(iterators)、函數對象(functors)、適配器(adapters)、分配器(allocators)

2、迭代器

迭代器是一種對象,它能夠用來遍歷STL容器中的部分或全部元素,每個迭代器對象代表容器中的確定的地址,所以可以認為迭代器其實就是用來指向容器中

據的指針,我們可以通過改變這個指針來遍歷容器中的所有元素。

3、容器

首先,我們必須理解一下什么是容器,對比我們生活當中的容器,例如水杯、桶、水瓶等等這些東西,其實他們都是容器,他們的一個共同點就是:都是用來

存放液體的,能夠用來存放一些東西;其實在我們的C++中說的這個容器其實作用也是用來存放"東西",但是存放的是數據,在C++中容器就是一種用來存放

數據的對象。

(1)C++中的容器其實是容器類實例化之后的一個具體的對象,那么可以辦這個對象看成就是一個容器。

(2)因為C++中容器類是基於類模板定義的,也就是我們這里說的STL(標准模板類)。為什么需要做成模板的形式呢?因為我們的容器中存放的數據類型其實

是相同的,如果就因為數據類型不同而要定義多個具體的類,這樣就不合適,而模板恰好又能夠解決這種問題,所以C++中的容器類是通過類模板的方式定義的

,也就是STL。

(3)容器還有另一個特點是容器可以自行擴展。在解決問題時我們常常不知道我們需要存儲多少個對象,也就是說我們不知道應該創建多大的內存空間來存放我們

的數據。顯然,數組在這一方面也力不從心。容器的優勢就在這里,它不需要你預先告訴它你要存儲多少對象,只要你創建一個容器對象,並合理的調用它所提

供的方法,所有的處理細節將由容器來自身完成。它可以為你申請內存或釋放內存,並且用最優的算法來執行您的命令。

(4)容器是隨着面向對象語言的誕生而提出的,容器類在面向對象語言中特別重要,甚至它被認為是早期面向對象語言的基礎。

4、容器的分類

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

我想說的是對於上面的每種類型的容器到底是是什么意思,其實沒必要去搞懂,沒什么價值,只要你能夠大概理解知道即可,知道每種容器類型下有哪些具體的容器

即可。

順序性容器:vector、deque、list

關聯性容器:set、multiset、map、multimap

容器適配器:stack、queue、

本文主要介紹vector、list和map 這3種容器。

5、vector向量

vector向量是一種順序行容器。相當於數組,但其大小可以不預先指定,並且自動擴展。它可以像數組一樣被操作,由於它的特性我們完全可以將vector 看作動態數組。

在創建一個vector 后,它會自動在內存中分配一塊連續的內存空間進行數據存儲,初始的空間大小可以預先指定也可以由vector 默認指定。當存儲的數據超過分配的

空間時vector 會重新分配一塊內存塊,但這樣的分配是很耗時的,在重新分配空間時它會做這樣的動作:

首先,vector 會申請一塊更大的內存塊;

然后,將原來的數據拷貝到新的內存塊中;

其次,銷毀掉原內存塊中的對象(調用對象的析構函數);

最后,將原來的內存空間釋放掉。

當vector保存的數據量很大時,如果此時進行插入數據導致需要更大的空間來存放這些數據量,那么將會大大的影響程序運行的效率,所以我們應該合理的使用vector。

 (1)初始化vector對象的方式:

vector<T> v1;     // 默認的初始化方式,內容為空

vector<T> v2(v1);   // v2是v1的一個副本

vector<T> v3(n, i)   // v3中包含了n個數值為i的元素

vector<T> v4(n);   // v4中包含了n個元素,每個元素的值都是0

(2)vector常用函數

empty():判斷向量是否為空,為空返回真,否則為假

begin():返回向量(數組)的首元素地址

end(): 返回向量(數組)的末元素的下一個元素的地址

clear():清空向量

front():返回得到向量的第一個元素的數據

back():返回得到向量的最后一個元素的數據

size():返回得到向量中元素的個數

push_back(數據):將數據插入到向量的尾部

pop_back():刪除向量尾部的數據

.....

(3)遍歷方式

vector向量支持兩種方式遍歷,因為可以認為vector是一種動態數組,所以可以使用數組下標的方式,也可以使用迭代器

 1 #include <iostream>
 2 #include <vector>
 3 #include <list>
 4 #include <map>
 5 
 6 using namespace std;
 7 
 8 int main(void)
 9 {
10     vector<int> vec;
11 
12     vec.push_back(1);
13     vec.push_back(2);
14     vec.push_back(3);
15     vec.push_back(4);
16     vec.push_back(5);
17 
18     cout << "向量的大小:" << vec.size() << endl;
19 
20     // 數組下標方式遍歷vector
21     for (int i = 0; i < vec.size(); i++)
22         cout << vec[i] << " ";
23     cout << endl;
24 
25     // 迭代器方式遍歷vector
26     vector<int>::iterator itor = vec.begin();
27     for (; itor != vec.end(); itor++)
28         cout << *itor << " ";
29     cout << endl;
30 
31     return 0;
32 }

 

6、雙向鏈表list

對於鏈表我不想多說了,我之前已經學過鏈表,對於一個雙向鏈表來說主要包括3個:指向前一個鏈表節點的前向指針、有效數據、指向后一個鏈表節點的后向指針

鏈表相對於vector向量來說的優點在於:(a)動態的分配內存,當需要添加數據的時候不會像vector那樣,先將現有的內存空間釋放,在次分配更大的空間,這樣的話

效率就比較低了。(b)支持內部插入、頭部插入和尾部插入

缺點:不能隨機訪問,不支持[]方式和vector.at()、占用的內存會多於vector(非有效數據占用的內存空間)

(1)初始化list對象的方式

list<int> L0;    //空鏈表

list<int> L1(3);   //建一個含三個默認值是0的元素的鏈表

list<int> L2(5,2); //建一個含五個元素的鏈表,值都是2

list<int> L3(L2); //L3是L2的副本

list<int> L4(L1.begin(),L1.end());    //c5含c1一個區域的元素[begin, end]。

(2)list常用函數

begin():返回list容器的第一個元素的地址

end():返回list容器的最后一個元素之后的地址

rbegin():返回逆向鏈表的第一個元素的地址(也就是最后一個元素的地址)

rend():返回逆向鏈表的最后一個元素之后的地址(也就是第一個元素再往前的位置)

front():返回鏈表中第一個數據值

back():返回鏈表中最后一個數據值

empty():判斷鏈表是否為空

size():返回鏈表容器的元素個數

clear():清除容器中所有元素

insert(pos,num):將數據num插入到pos位置處(pos是一個地址)

insert(pos,n,num):在pos位置處插入n個元素num

erase(pos):刪除pos位置處的元素

push_back(num):在鏈表尾部插入數據num

pop_back():刪除鏈表尾部的元素

push_front(num):在鏈表頭部插入數據num

pop_front():刪除鏈表頭部的元素

sort():將鏈表排序,默認升序

......

(3)遍歷方式

雙向鏈表list支持使用迭代器正向的遍歷,也支持迭代器逆向的遍歷,但是不能使用 [] 索引的方式進行遍歷。

 1 #include <iostream>
 2 #include <vector>
 3 #include <list>
 4 #include <map>
 5 
 6 using namespace std;
 7 
 8 int main(void)
 9 {
10     list<int> l1;
11 
12     // 插入元素方式演示
13     l1.push_front(1);            // 頭部插入
14     l1.push_back(2);             // 尾部插入
15     l1.insert(l1.begin(), 3);    // 開始位置插入
16     l1.insert(l1.end(), 4);        // 結束位置插入
17 
18     cout << "鏈表是否為空:" << l1.empty() << endl;
19     cout << "list鏈表中元素個數:" << l1.size() << endl;
20     cout << "list鏈表第一個元素:" << l1.front() << endl;
21     cout << "list鏈表最后一個元素:" << l1.back() << endl;
22 
23     // 遍歷鏈表正向
24     list<int>::iterator itor = l1.begin();
25     for (; itor != l1.end(); itor++)
26         cout << *itor << " ";
27     cout << endl;
28 
29     // 遍歷鏈表逆向
30     list<int>::reverse_iterator reitor = l1.rbegin();
31     for (; reitor != l1.rend(); reitor++)
32         cout << *reitor << " ";
33     cout << endl;
34 
35     // 將鏈表排序
36     l1.sort();
37     itor = l1.begin();
38     cout << "重新排序之后正向遍歷:";
39     for (; itor != l1.end(); itor++)
40         cout << *itor << " ";
41     cout << endl;
42 
43     // 清除容器中的所有元素
44     l1.clear();
45     cout << "清除容器所有元素之后大小:" << l1.size() << endl;
46 
47     return 0;
48 }
View Code

代碼運行結果:

7、map

Map是STL的一個關聯容器,它提供一對一(其中第一個可以稱為關鍵字,每個關鍵字只能在map中出現一次,第二個可能稱為該關鍵字的值)的數據處理能力,由於這個特性,它完成有可能在我們處理一對一數據的時候,在編程上提供快速通道。map內部自建一顆紅黑樹(一 種非嚴格意義上的平衡二叉樹),這顆樹具有對數據自動排序的功能,所以在map內部所有的數據都是有序的。至於二叉樹這種數據結構,本人暫時沒有任何了解。在map這個容器中,提供一種“鍵- 值”關系的一對一的數據存儲能力。其“鍵”在容器中不可重復,且按一定順序排列,至於怎么排列,那么紅黑樹這種數據結構的特性了。

 

(1)初始化map對象的方式

map<int, string> m1 = { { 1, "guangzhou" }, { 2, "shenzhen" }, { 3, "changsha" } };   // 實例化一個map容器,還有3組數據
map<char, string> m2;             // 實例化一個空map容器

(2)map常用函數

begin():返回容器第一個元素的迭代器

end():返回容器最后一個元素之后的迭代器

rbegin():

rend():

clera():清除容器中所有元素

empty():判斷容器是否為空

insert(p1):插入元素  p1 是通過pair函數建立的映射關系對

insert(pair<char, string>('S', "shenzhen")): 插入元素

size():返回容器中元素的個數

count():返回指定鍵對應的數據的出現的次數

get_allocator():返回map的配置器

swap():交換兩個map容器的元素

.....

(3)遍歷方式

map容器支持迭代器正向方式遍歷和迭代器反向方式遍歷,同時也支持 [] 方式訪問數據,[]中的索引值是鍵值,這個一定要清楚

 1 #include <iostream>
 2 #include <stdio.h>
 3 #include <string>
 4 #include <stdlib.h>
 5 #include <vector>
 6 #include <list>
 7 #include <map>
 8 
 9 using namespace std;
10 
11 int main(void)
12 {
13     map<int, string> m1 = { { 1, "guangzhou" }, { 2, "shenzhen" }, { 3, "changsha" } };
14     map<char, string> m2;
15 
16     // 建立映射關系對
17     pair<char, string> p1('G', "guangzhou");
18     pair<char, string> p2('S', "guangzhou");
19     pair<char, string> p3('C', "changsha");
20 
21     // 插入數據
22     m2.insert(p1);
23     m2.insert(p2);
24     m2.insert(p3);
25 
26     cout << "map容器m1元素個數:" << m1.size() << endl;
27     cout << "map容器m2元素個數:" << m2.size() << endl;
28 
29     // 采用 [] 方式打印數據
30     cout << m1[1] << " " << m1[2] << " " << m1[3] << endl;
31     cout << m2['G'] << " " << m2['S'] << " " << m2['C'] << endl;
32 
33     // 迭代器正向方式遍歷
34     map<int, string>::iterator itor = m1.begin();
35     for (; itor != m1.end(); itor++)
36     {
37         cout << itor->first << ",";
38         cout << itor->second << endl;
39     }
40 
41     // 迭代器反向方式遍歷
42     map<char, string>::reverse_iterator reitor = m2.rbegin();
43     for (; reitor != m2.rend(); reitor++)
44     {
45         cout << reitor->first << ",";
46         cout << reitor->second << endl;
47     }
48 
49     // 清空容器
50     m1.clear();
51     m2.clear();
52    
53     return 0;
54 }
View Code

 

8、順序性容器和關聯容器(本段來自其他博客,在此感謝)

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

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

 

 

參考博客: http://www.cnblogs.com/xkfz007/articles/2534249.html

                     http://www.cnblogs.com/scandy-yuan/archive/2013/01/08/2851324.html

 


免責聲明!

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



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