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 }
代碼運行結果:
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 }
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