STL(Standard Template Library,標準模板庫)
目錄
一、STL 六大組件簡介
STL提供了六大組件,彼此之間可以組合套用,這六大組件分別是:容器、算法、迭代器、仿函數、適配器(配接器)、空間配置器。
- 容器:各種數據結構,如vector、list、deque、set、map等,用來存放數據,從實現角度來看,STL容器是一種class template。
- 算法:各種常用的算法,如sort、find、copy、for_each。從實現的角度來看,STL算法是一種function tempalte.
- 迭代器:扮演了容器與算法之間的膠合劑,共有五種類型,從實現角度來看,迭代器是一種將operator* , operator-> , operator++,operator--等指針相關操作予以重載的class template. 所有STL容器都附帶有自己專屬的迭代器,只有容器的設計者才知道如何遍歷自己的元素。原生指針也是一種迭代器。
- 仿函數:行爲類似函數,可作爲算法的某種策略。從實現角度來看,仿函數是一種重載了operator()的class 或者class template
- 適配器:一種用來修飾容器或者仿函數或迭代器接口的東西。
- 空間配置器:負責空間的配置與管理。從實現角度看,配置器是一個實現了動態空間配置、空間管理、空間釋放的class tempalte.
二、 string容器
2.1 string容器基本概念
C風格字符串(以空字符結尾的字符數組)太過複雜難於掌握,不適合大程序的開發,所以C++標準庫定義了一種string類,定義在頭文件<string>。
String和c風格字符串對比:
- Char*是一個指針,String是一個類
string封裝了char*,管理這個字符串,是一個char*型的容器。
- String封裝了很多實用的成員方法
查找find,拷貝copy,刪除delete 替換replace,插入insert
- 不用考慮內存釋放和越界
string管理char*所分配的內存。每一次string的複製,取值都由string類負責維護,不用擔心複製越界和取值越界等。
2.2 string 構造函數
string();//創建一個空的字符串 例如: string str; string(const string& str);//使用一個string對象初始化另一個string對象 string(const char* s);//使用字符串s初始化 string(int n, char c);//使用n個字符c初始化 |
2.3 string基本賦值操作
string& operator=(const char* s);//char*類型字符串 賦值給當前的字符串 string& operator=(const string &s);//把字符串s賦給當前的字符串 string& operator=(char c);//字符賦值給當前的字符串 string& assign(const char *s);//把字符串s賦給當前的字符串 string& assign(const char *s, int n);//把字符串s的前n個字符賦給當前的字符串 string& assign(const string &s);//把字符串s賦給當前字符串 string& assign(int n, char c);//用n個字符c賦給當前字符串 string& assign(const string &s, int start, int n);//將s從start開始n個字符賦值給字符串 |
2.4 string存取字符操作
char& operator[](int n);//通過[]方式取字符 char& at(int n);//通過at方法獲取字符 |
2.5 string拼接操作
string& operator+=(const string& str);//重載+=操作符 string& operator+=(const char* str);//重載+=操作符 string& operator+=(const char c);//重載+=操作符 string& append(const char *s);//把字符串s連接到當前字符串結尾 string& append(const char *s, int n);//把字符串s的前n個字符連接到當前字符串結尾 string& append(const string &s);//同operator+=() string& append(const string &s, int pos, int n);//把字符串s中從pos開始的n個字符連接到當前字符串結尾 string& append(int n, char c);//在當前字符串結尾添加n個字符c |
2.6 string查找和替換
int find(const string& str, int pos = 0) const; //查找str第一次出現位置,從pos開始查找 int find(const char* s, int pos = 0) const; //查找s第一次出現位置,從pos開始查找 int find(const char* s, int pos, int n) const; //從pos位置查找s的前n個字符第一次位置 int find(const char c, int pos = 0) const; //查找字符c第一次出現位置 int rfind(const string& str, int pos = npos) const;//查找str最後一次位置,從pos開始查找 int rfind(const char* s, int pos = npos) const;//查找s最後一次出現位置,從pos開始查找 int rfind(const char* s, int pos, int n) const;//從pos查找s的前n個字符最後一次位置 int rfind(const char c, int pos = 0) const; //查找字符c最後一次出現位置 string& replace(int pos, int n, const string& str); //替換從pos開始n個字符爲字符串str string& replace(int pos, int n, const char* s); //替換從pos開始的n個字符爲字符串s |
2.7 string比較操作
/* compare函數在>時返回 1,<時返回 -1,==時返回 0。 比較區分大小寫,比較時參考字典順序,排越前面的越小。 大寫的A比小寫的a小。 */ int compare(const string &s) const;//與字符串s比較 int compare(const char *s) const;//與字符串s比較 |
2.8 string子串
string substr(int pos = 0, int n = npos) const;//返回由pos開始的n個字符組成的字符串 |
2.9 string插入和刪除操作
string& insert(int pos, const char* s); //插入字符串 string& insert(int pos, const string& str); //插入字符串 string& insert(int pos, int n, char c);//在指定位置插入n個字符c string& erase(int pos, int n = npos);//刪除從Pos開始的n個字符 |
2.10 string和c-style字符串轉換
//string 轉 char* string str = "itcast"; const char* cstr = str.c_str(); //char* 轉 string char* s = "itcast"; string str(s); |
提示:
|
string s = "abcdefg"; char& a = s[2]; char& b = s[3];
a = '1'; b = '2';
cout << s << endl; cout << (int*)s.c_str() << endl;
s = "pppppppppppppppppppppppp";
//a = '1'; //b = '2';
cout << s << endl; cout << (int*)s.c_str() << endl; |
三、vector容器
vector的數據安排以及操作方式,與array非常相似,兩者的唯一差別在於空間的運用的靈活性。Array是靜態空間,一旦配置了就不能改變,要換大一點或者小一點的空間,可以,一切瑣碎得由自己來,首先配置一塊新的空間,然後將舊空間的數據搬往新空間,再釋放原來的空間。Vector是動態空間。
Vector維護一個線性空間,所以不論元素的型別如何,普通指針都可以作爲vector的迭代器,因爲vector迭代器所需要的操作行爲,如operaroe*, operator->, operator++, operator--, operator+, operator-, operator+=, operator-=, 普通指針天生具備。Vector支持隨機存取,而普通指針正有着這樣的能力。所以vector提供的是隨機訪問迭代器
|
3.1 vector構造函數
vector<T> v; //採用模板實現類實現,默認構造函數 vector(v.begin(), v.end());//將v[begin(), end())區間中的元素拷貝給本身。 vector(n, elem);//構造函數將n個elem拷貝給本身。 vector(const vector &vec);//拷貝構造函數。
//例子 使用第二個構造函數 我們可以... int arr[] = {2,3,4,1,9}; vector<int> v1(arr, arr + sizeof(arr) / sizeof(int)); |
3.2 vector常用賦值操作
assign(beg, end);//將[beg, end)區間中的數據拷貝賦值給本身。 assign(n, elem);//將n個elem拷貝賦值給本身。 vector& operator=(const vector &vec);//重載等號操作符 swap(vec);// 將vec與本身的元素互換。 |
3.3 vector大小操作
size();//返回容器中元素的個數 empty();//判斷容器是否爲空 resize(int num);//重新指定容器的長度爲num,若容器變長,則以默認值填充新位置。如果容器變短,則末尾超出容器長度的元素被刪除。 resize(int num, elem);//重新指定容器的長度爲num,若容器變長,則以elem值填充新位置。如果容器變短,則末尾超出容器長>度的元素被刪除。 capacity();//容器的容量 reserve(int len);//容器預留len個元素長度,預留位置不初始化,元素不可訪問。 |
3.4 vector數據存取操作
at(int idx); //返回索引idx所指的數據,如果idx越界,拋出out_of_range異常。 operator[];//返回索引idx所指的數據,越界時,運行直接報錯 front();//返回容器中第一個數據元素 back();//返回容器中最後一個數據元素 |
3.5 vector插入和刪除操作
insert(const_iterator pos, int count,ele);//迭代器指向位置pos插入count個元素ele. push_back(ele); //尾部插入元素ele pop_back();//刪除最後一個元素 erase(const_iterator start, const_iterator end);//刪除迭代器從start到end之間的元素 erase(const_iterator pos);//刪除迭代器指向的元素 clear();//刪除容器中所有元素 |
3.6 巧用swap,收縮內存空間
3.7 reserve預留空間
#define _CRT_SECURE_NO_WARNINGS #include<iostream> #include<vector> using namespace std;
int main(){
vector<int> v;
//預先開闢空間 v.reserve(100000);
int* pStart = NULL; int count = 0; for (int i = 0; i < 100000;i ++){ v.push_back(i); if (pStart != &v[0]){ pStart = &v[0]; count++; } }
cout << "count:" << count << endl;
system("pause"); return EXIT_SUCCESS; } |
四、deque容器
Vector容器是單向開口的連續內存空間,deque則是一種雙向開口的連續線性空間。所謂的雙向開口,意思是可以在頭尾兩端分別做元素的插入和刪除操作,當然,vector容器也可以在頭尾兩端插入元素,但是在其頭部操作效率奇差,無法被接受。
4.1 deque容器實現原理
queue沒有迭代器Queue所有元素的進出都必須符合”先進先出”的條件,只有queue的頂端元素,纔有機會被外界取用。Queue不提供遍歷功能,也不提供迭代器。
Deque容器是連續的空間,至少邏輯上看來如此,連續現行空間總是令我們聯想到array和vector,array無法成長,vector雖可成長,卻只能向尾端成長,而且其成長其實是一個假象,事實上(1) 申請更大空間 (2)原數據複製新空間 (3)釋放原空間 三步驟,如果不是vector每次配置新的空間時都留有餘裕,其成長假象所帶來的代價是非常昂貴的。
Deque是由一段一段的定量的連續空間構成。一旦有必要在deque前端或者尾端增加新的空間,便配置一段連續定量的空間,串接在deque的頭端或者尾端。Deque最大的工作就是維護這些分段連續的內存空間的整體性的假象,並提供隨機存取的接口,避開了重新配置空間,複製,釋放的輪迴,代價就是複雜的迭代器架構。
既然deque是分段連續內存空間,那麼就必須有中央控制,維持整體連續的假象,數據結構的設計及迭代器的前進後退操作頗爲繁瑣。Deque代碼的實現遠比vector或list都多得多。
Deque採取一塊所謂的map(注意,不是STL的map容器)作爲主控,這裏所謂的map是一小塊連續的內存空間,其中每一個元素(此處成爲一個結點)都是一個指針,指向另一段連續性內存空間,稱作緩衝區。緩衝區纔是deque的存儲空間的主體。
4.2 deque構造函數
deque<T> deqT;//默認構造形式 deque(beg, end);//構造函數將[beg, end)區間中的元素拷貝給本身。 deque(n, elem);//構造函數將n個elem拷貝給本身。 deque(const deque &deq);//拷貝構造函數。 |
4.3 deque賦值操作
assign(beg, end);//將[beg, end)區間中的數據拷貝賦值給本身。 assign(n, elem);//將n個elem拷貝賦值給本身。 deque& operator=(const deque &deq); //重載等號操作符 swap(deq);// 將deq與本身的元素互換 |
4.4 deque大小操作
deque.size();//返回容器中元素的個數 deque.empty();//判斷容器是否爲空 deque.resize(num);//重新指定容器的長度爲num,若容器變長,則以默認值填充新位置。如果容器變短,則末尾超出容器長度的元素被刪除。 deque.resize(num, elem); //重新指定容器的長度爲num,若容器變長,則以elem值填充新位置,如果容器變短,則末尾超出容器長度的元素被刪除。 |
4.5 deque雙端插入和刪除操作
push_back(elem);//在容器尾部添加一個數據 push_front(elem);//在容器頭部插入一個數據 pop_back();//刪除容器最後一個數據 pop_front();//刪除容器第一個數據 |
4.6 deque數據存取
at(idx);//返回索引idx所指的數據,如果idx越界,拋出out_of_range。 operator[];//返回索引idx所指的數據,如果idx越界,不拋出異常,直接出錯。 front();//返回第一個數據。 back();//返回最後一個數據 |
4.7 deque插入操作
insert(pos,elem);//在pos位置插入一個elem元素的拷貝,返回新數據的位置。 insert(pos,n,elem);//在pos位置插入n個elem數據,無返回值。 insert(pos,beg,end);//在pos位置插入[beg,end)區間的數據,無返回值。 |
4.8 deque刪除操作
clear();//移除容器的所有數據 erase(beg,end);//刪除[beg,end)區間的數據,返回下一個數據的位置。 erase(pos);//刪除pos位置的數據,返回下一個數據的位置。 |
五、stack容器
stack是一種先進後出(First In Last Out,FILO)的數據結構,它只有一個出口,形式如圖所示。stack容器允許新增元素,移除元素,取得棧頂元素,但是除了最頂端外,沒有任何其他方法可以存取stack的其他元素。換言之,stack不允許有遍歷行爲。
stack沒有迭代器,Stack所有元素的進出都必須符合”先進後出”的條件,只有stack頂端的元素,纔有機會被外界取用。Stack不提供遍歷功能,也不提供迭代器。
5.1stack構造函數
stack<T> stkT;//stack採用模板類實現, stack對象的默認構造形式: stack(const stack &stk);//拷貝構造函數 |
5.2 stack賦值操作
stack& operator=(const stack &stk);//重載等號操作符 |
5.3 stack數據存取操作
push(elem);//向棧頂添加元素 pop();//從棧頂移除第一個元素 top();//返回棧頂元素 |
5.4 stack大小操作
empty();//判斷堆棧是否爲空 size();//返回堆棧的大小 |
六、queue容器
Queue是一種先進先出的數據結構,它有兩個出口,queue容器允許從一端新增元素,從另一端移除元素。
Queue所有元素的進出都必須符合”先進先出”的條件,只有queue的頂端元素,纔有機會被外界取用。Queue不提供遍歷功能,也不提供迭代器。
6.1 queue構造函數
queue<T> queT;//queue採用模板類實現,queue對象的默認構造形式: queue(const queue &que);//拷貝構造函數 |
6.2 queue存取、插入和刪除操作
push(elem);//往隊尾添加元素 pop();//從隊頭移除第一個元素 back();//返回最後一個元素 front();//返回第一個元素 |
6.3 queue賦值操作
queue& operator=(const queue &que);//重載等號操作符 |
6.4 queue大小操作
empty();//判斷隊列是否爲空 size();//返回隊列的大小 |
七、list容器
List容器是一個雙向鏈表。
鏈表是一種物理存儲單元上非連續、非順序的存儲結構,數據元素的邏輯順序是通過鏈表中的指針鏈接次序實現的。鏈表由一系列結點(鏈表中每一個元素稱爲結點)組成,結點可以在運行時動態生成。每個結點包括兩個部分:一個是存儲數據元素的數據域,另一個是存儲下一個結點地址的指針域。
- 採用動態存儲分配,不會造成內存浪費和溢出
- 鏈表執行插入和刪除操作十分方便,修改指針即可,不需要移動大量元素
- 鏈表靈活,但是空間和時間額外耗費較大
7.1 list構造函數
list<T> lstT;//list採用採用模板類實現,對象的默認構造形式: list(beg,end);//構造函數將[beg, end)區間中的元素拷貝給本身。 list(n,elem);//構造函數將n個elem拷貝給本身。 list(const list &lst);//拷貝構造函數。 |
7.2 list數據元素插入和刪除操作
push_back(elem);//在容器尾部加入一個元素 pop_back();//刪除容器中最後一個元素 push_front(elem);//在容器開頭插入一個元素 pop_front();//從容器開頭移除第一個元素 insert(pos,elem);//在pos位置插elem元素的拷貝,返回新數據的位置。 insert(pos,n,elem);//在pos位置插入n個elem數據,無返回值。 insert(pos,beg,end);//在pos位置插入[beg,end)區間的數據,無返回值。 clear();//移除容器的所有數據 erase(beg,end);//刪除[beg,end)區間的數據,返回下一個數據的位置。 erase(pos);//刪除pos位置的數據,返回下一個數據的位置。 remove(elem);//刪除容器中所有與elem值匹配的元素。 |
7.3 list大小操作
size();//返回容器中元素的個數 empty();//判斷容器是否爲空 resize(num);//重新指定容器的長度爲num, 若容器變長,則以默認值填充新位置。 如果容器變短,則末尾超出容器長度的元素被刪除。 resize(num, elem);//重新指定容器的長度爲num, 若容器變長,則以elem值填充新位置。 如果容器變短,則末尾超出容器長度的元素被刪除。 |
7.4 list賦值操作
assign(beg, end);//將[beg, end)區間中的數據拷貝賦值給本身。 assign(n, elem);//將n個elem拷貝賦值給本身。 list& operator=(const list &lst);//重載等號操作符 swap(lst);//將lst與本身的元素互換。 |
7.5 list數據的存取
front();//返回第一個元素。 back();//返回最後一個元素。 |
7.6 list反轉排序
reverse();//反轉鏈表,比如lst包含1,3,5元素,運行此方法後,lst就包含5,3,1元素。 sort(); //list排序 |
八、set/multiset容器
Set的特性是。所有元素都會根據元素的鍵值自動被排序。Set的元素不像map那樣可以同時擁有實值和鍵值,set的元素即是鍵值又是實值。Set不允許兩個元素有相同的鍵值。
我們可以通過set的迭代器改變set元素的值嗎?不行,因爲set元素值就是其鍵值,關係到set元素的排序規則。如果任意改變set元素值,會嚴重破壞set組織。換句話說,set的iterator是一種const_iterator.
set擁有和list某些相同的性質,當對容器中的元素進行插入操作或者刪除操作的時候,操作之前所有的迭代器,在操作完成之後依然有效,只有被刪除的那個元素的迭代器才無效。
multiset特性及用法和set完全相同,唯一的差別在於它允許鍵值重複。set和multiset的底層實現是紅黑樹,紅黑樹爲平衡二叉樹的一種。
8.1 set構造函數
set<T> st;//set默認構造函數: mulitset<T> mst; //multiset默認構造函數: set(const set &st);//拷貝構造函數 |
8.2 set賦值操作
set& operator=(const set &st);//重載等號操作符 swap(st);//交換兩個集合容器 |
8.3 set大小操作
size();//返回容器中元素的數目 empty();//判斷容器是否爲空 |
8.4 set插入和刪除操作
insert(elem);//在容器中插入元素。 clear();//清除所有元素 erase(pos);//刪除pos迭代器所指的元素,返回下一個元素的迭代器。 erase(beg, end);//刪除區間[beg,end)的所有元素 ,返回下一個元素的迭代器。 erase(elem);//刪除容器中值爲elem的元素。 |
8.5 set查找操作
find(key);//查找鍵key是否存在,若存在,返回該鍵的元素的迭代器;若不存在,返回set.end(); count(key);//查找鍵key的元素個數 lower_bound(keyElem);//返回第一個key>=keyElem元素的迭代器。 upper_bound(keyElem);//返回第一個key>keyElem元素的迭代器。 equal_range(keyElem);//返回容器中key與keyElem相等的上下限的兩個迭代器。 |
8.6 對組(pair)
對組(pair)將一對值組合成一個值,這一對值可以具有不同的數據類型,兩個值可以分別用pair的兩個公有屬性first和second訪問。
//第一種方法創建一個對組
pair<string, int> pair1(string("name"), 20);
cout << pair1.first << endl; //訪問pair第一個值
cout << pair1.second << endl;//訪問pair第二個值
//第二種
pair<string, int> pair2 = make_pair("name", 30);
cout << pair2.first << endl;
cout << pair2.second << endl;
//pair=賦值
pair<string, int> pair3 = pair2;
cout << pair3.first << endl;
cout << pair3.second << endl;
九、map/multimap容器
Map的特性是,所有元素都會根據元素的鍵值自動排序。Map所有的元素都是pair,同時擁有實值和鍵值,pair的第一元素被視爲鍵值,第二元素被視爲實值,map不允許兩個元素有相同的鍵值。
我們可以通過map的迭代器改變map的鍵值嗎?答案是不行,因爲map的鍵值關係到map元素的排列規則,任意改變map鍵值將會嚴重破壞map組織。如果想要修改元素的實值,那麼是可以的。
Map和list擁有相同的某些性質,當對它的容器元素進行新增操作或者刪除操作時,操作之前的所有迭代器,在操作完成之後依然有效,當然被刪除的那個元素的迭代器必然是個例外。
Multimap和map的操作類似,唯一區別multimap鍵值可重複。
Map和multimap都是以紅黑樹爲底層實現機制。
9.1 map構造函數
map<T1, T2> mapTT;//map默認構造函數: map(const map &mp);//拷貝構造函數 |
9.2 map賦值操作
map& operator=(const map &mp);//重載等號操作符 swap(mp);//交換兩個集合容器 |
9.3 map大小操作
size();//返回容器中元素的數目 empty();//判斷容器是否爲空 |
9.4 map插入數據元素操作
map.insert(...); //往容器插入元素,返回pair<iterator,bool> map<int, string> mapStu; // 第一種 通過pair的方式插入對象 mapStu.insert(pair<int, string>(3, "小張")); // 第二種 通過pair的方式插入對象 mapStu.inset(make_pair(-1, "校長")); // 第三種 通過value_type的方式插入對象 mapStu.insert(map<int, string>::value_type(1, "小李")); // 第四種 通過數組的方式插入值 mapStu[3] = "小劉"; mapStu[5] = "小王"; |
9.5 map刪除操作
clear();//刪除所有元素 erase(pos);//刪除pos迭代器所指的元素,返回下一個元素的迭代器。 erase(beg,end);//刪除區間[beg,end)的所有元素 ,返回下一個元素的迭代器。 erase(keyElem);//刪除容器中key爲keyElem的對組。 |
9.6 map查找操作
find(key);//查找鍵key是否存在,若存在,返回該鍵的元素的迭代器;/若不存在,返回map.end(); count(keyElem);//返回容器中key爲keyElem的對組個數。對map來說,要麼是0,要麼是1。對multimap來說,值可能大於1。 lower_bound(keyElem);//返回第一個key>=keyElem元素的迭代器。 upper_bound(keyElem);//返回第一個key>keyElem元素的迭代器。 equal_range(keyElem);//返回容器中key與keyElem相等的上下限的兩個迭代器。 |