順序容器2
順序容器類型:
vector 可變大小數組。支持快速隨機訪問。在尾部之外的位置插入或刪除元素可能很慢
deque 雙端隊列。支持快速隨機訪問。在頭尾位置插入/刪除速度很快
list 雙向列表。只支持雙向順序訪問。在 list 中任何位置進行插入/刪除操作速度都很快
forward_list 單向鏈表。只支持單向順序訪問。在鏈表任何位置進行插入/刪除操作都很快
array 固定大小數組。支持快速隨機訪問。不能添加或刪除元素
string 與 vector 相似的容器,但專門用於保存字符。隨機訪問快。在尾部插入/刪除速度很快
容器操作(後面再提具體每個容器操作對元素的其他限制):
類型別名
iterator 此容器的叠代器類型
const_iterator 可以讀取元素,但不能修改元素的叠代器類型
size_type 無符號整數類型,足夠保存此種容器類型最大可能容器大小
difference_type 帶符號整數類型,足夠保存兩個叠代器之間的距離
value_type 元素類型
reference 元素的左值類型,與 value_type& 含義相同
const_reference 元素的 const 左值類型,即 const value_type&
構造函數
C c; 默認構造函數,構造空容器
C c1(c2); 構造 c2 的拷貝 c1
C c(b, e); 構造 c,將叠代器 b 到 e 指定的範圍內的元素拷貝到 c(array 不支持)
C c{a, b, c, d...} 列表初始化 c
賦值與 swap
c1 = c2; 將 c1 中的元素替換為 c2 中的元素
c1 = {a, b, c, d...} 將 c1 中的元素替換為列表中元素(不適用於 array)
a.swap(b); 交換 a 和 b 的元素
swap(a, b); 等價於 a.swap(b)
大小
c.size(); c 中元素的數目(不支持 forward_list)
c.max_size(); c 中可保存的最大數目的元素數目
c.empty(); 若 c 中存儲了元素,返回 false,否則返回 true
添加/刪除元素(不適用於 array)
註:在不同容器中,這些操作的接口都不同
c.insert(args); 將 args 中的元素拷貝進 c
c.emplace(inits); 使用 inits 構造 c 中的一個元素
c.erase(args); 刪除 args 指定的元素
c.clear(); 刪除 c 中所有元素,返回 void
關系運算符
==,!= 所有容器都支持
<,<=,>,>= 關系運算(無序關聯容器不支持)
獲取叠代器
c.begin(),c.end() 返回 c 的首元素和尾元素之後位置的叠代器
c.cbegin(),c.cend() 返回 const_iterator
反向容器的額外成員(不支持 forward_list)
reverse_iterator 按逆序尋址元素的叠代器
const_reverse_iterator 不能修改元素的逆序叠代器
c.rbegin(),c.rend() 返回指向 c 尾元素和首元素之前位置的叠代器
c.crbegin(),c.crend() 返回 const_reverse_iterator
雖然我們可以在容器中保存幾乎任何類型,但某些容器操作對元素類型有自己的特殊要求。我們可以為不支持特定操作需求的類型定義容器,但這種情況下就只能使用那些沒有特殊要求的容器操作了:
1 #include <iostream> 2 #include <vector> 3 using namespace std; 4 5 class no_default{//no_default 是一個沒有默認構造函數的類型 6 int x; 7 public: 8 no_default(int a) : x(a){} 9 ~no_default(){} 10 }; 11 12 int main(void){ 13 int init = 1; 14 std::vector<no_default> v1(10, init);//正確,提供了元素初始化器(執行結果是v1中有10個1) 15 // std::vector<no_default> v2(10);//錯誤,必須提供一個元素初始化器 16 return 0; 17 }View Code
begin 和 end 成員:
1 #include <iostream> 2 #include <list> 3 using namespace std; 4 5 int main(void){ 6 list<string> a = {"Milton", "Shakespeare", "Austen"}; 7 auto it1 = a.begin(); //list<string>::iterator 8 auto it2 = a.rbegin();//list<string>::reverse_iterator 9 auto it3 = a.cbegin();//list<string>::const_iterator 10 auto it4 = a.crbegin();//list<string>::const_reverse_iterator 11 12 cout << *it1 << endl;//Milton 13 it1++; 14 cout << *it1 << endl;//Shakespeare 15 *it1 = "jflsfk"; 16 cout << *it1 << endl;//jflsfk 17 cout << endl; 18 19 cout << *it2 << endl;//Austen 20 it2++; 21 cout << *it2 << endl;//jflsfk 22 *it2 = "Shakespeare"; 23 cout << *it2 << endl;//Shakespeare 24 cout << endl; 25 26 cout << *it3 << endl;//Milton 27 it3++; 28 cout << *it3 << endl;//Shakespeare 29 // *it3 = "hello";//錯誤,常量叠代器類似於具有底層const的指針 30 cout << endl; 31 32 cout << *it4 << endl;//Austen 33 it4++; 34 cout << *it4 << endl;//Shakespeare 35 // *it4 = "hello";//錯誤,常量叠代器類似於具有底層const的指針 36 cout << endl; 37 38 const list<string> b = {"hello", "gg", "yy"}; 39 auto it5 = b.begin(); 40 auto it6 = b.rbegin(); 41 auto it7 = b.cbegin(); 42 auto it8 = b.crbegin(); 43 44 cout << *it5 << endl;//hello 45 it5++; 46 cout << *it5 << endl;//gg 47 // *it5 = "aa";//錯誤,const對象的begin()返回的是一個const_iterator 48 49 // b = a;//錯誤 50 51 list<string> const c = {"hello", "gg", "yy"};//可以將list<string>看作int,那麽b和c分別相當於const int 和 int const,是等價的,都是頂層const 52 53 // c = a;//錯誤 54 55 return 0; 56 }View Code
通過上面的列子還可以發現不以 c 開頭的函數都是被重載過的。也就是說,實際上有兩個名為 begin 的成員。一個是 const 成員,返回容器的 const_iterator 類型。另一個是非常量成員,返回容器的 iterator 類型。rbegin,end,rend 的情況類型。當我們對一個非常量成員調用這些成員時,返回的是 const 版本。只有在對一個 const 對象調用這些函數時,才會得到一個 const 版本。與 const 指針和引用類似,可以將一個普通的 iterator 轉換成對應的 const_iterator,反之則不行。
而以 c 開頭版本是 c++11 標準引入的,用以支持 auto 與 begin 和 end 函數結合使用。無論對常量容器還是非常量容器,其返回的都是 const_iterator。
即 auto 與 begin 或 end 結合時,獲得的叠代器類型依賴於容器類型,而以 c 開頭的版本是可以獲得 const_iterator 的,無論容器的類型是否為 const。
將一個容器初始化為另一個容器的拷貝:
1 #include <iostream> 2 #include <list> 3 #include <vector> 4 #include <deque> 5 #include <forward_list> 6 using namespace std; 7 8 int main(void){ 9 list<string> authors = {"Milton", "Shakespeare", "Austen"};//列表初始化 10 vector<const char*> gel = {"a", "an", "the"};//列表初始化 11 12 list<string> list1(authors);//正確,類型都匹配 13 // deque<string> a(authors);//錯誤,容器類型不匹配 14 // vector<string> v(gel);//錯誤,元素類型不匹配 15 16 forward_list<string> f(gel.begin(), gel.end());//正確,只元素能相互轉化即可 17 18 return 0; 19 }View Code
為了創建一個容器為另一個容器的拷貝,兩個容器的類型及其元素類型必須匹配。不過,當傳遞叠代器參數來拷貝一個範圍時,就不要求容器類型是相同的了。而且,新容器和原容器中的元素也可以不同,只要能將拷貝的元素轉換為要初始化的容器元素類型即可。
標準庫 array:
1 #include <iostream> 2 #include <array> 3 using namespace std; 4 5 int main(void){ 6 array<int, 10> a;//類型為保存10個int的數組 7 array<string, 10> b;//類型為保存10個string的數組 8 9 array<int, 10>::size_type i;//數組類型包括元素類型和大小 10 // array<int>::size_type j;//錯誤,array<int>不是一個類型 11 12 array<int, 3> c = {1, 2, 3};//列表初始化 13 array<int, 5> d = {1};//d[0]為1,剩余元素都為0 14 //這兩點和內置數組是一樣的 15 16 int e[5] = {1, 2, 3, 4, 5}; 17 // int cpy[5] = e;//錯誤,內置數組不支持拷貝或賦值 18 array<int, 5> f = {1, 2, 3, 4, 5}; 19 array<int, 5> copy = f;//正確,只要數組類型匹配即合法 20 // copy = e;//錯誤,內置數組不能被賦值給array 21 // array<int, 4> g = f;//錯誤,數組大小不一致 22 23 return 0; 24 }View Code
定義以及使用 array 類型時必須指定容器的元素類型和大小
數組類型包含元素的類型和大小,因此拷貝初始化時不僅要求初始值的類型必須與要創建的容器相同,大小也要相同
array 的列表初始化規則和內置數組一致
assign:
1 #include <iostream> 2 #include <list> 3 #include <vector> 4 using namespace std; 5 6 int main(void){ 7 list<string> names; 8 vector<const char*> old_style; 9 // names = old_style;//錯誤,容器類型不匹配不能賦值 10 names.assign(old_style.cbegin(), old_style.cend());//正確,可以將const char*轉化成string 11 12 list<string> a(1);//一個元素,為空string 13 a.assign(10, "hello");//10個元素,每個都是hello 14 for(const auto indx : a){ 15 cout << indx << " "; 16 } 17 cout << endl; 18 19 a.clear(); 20 a.insert(a.begin(), 10, "hello");//通過輸出可以發現這兩條語句與前面的兩條語句等效 21 for(const auto indx : a){ 22 cout << indx << " "; 23 } 24 cout << endl; 25 return 0; 26 }View Code
賦值運算符要求左邊和右邊的運算對象具有相同的類型。此外順序容器(array除外)還定義了一個名為 assign 的成員,允許我們從一個不同但相容的類型賦值,或從容器的一個子序列賦值。
需要註意的是:賦值相關運算會導致指向左邊容器內部的叠代器、引用和指針失效。
swap:
1 #include <iostream> 2 #include <vector> 3 using namespace std; 4 5 int main(void){ 6 std::vector<string> v1(10, "f"); 7 std::vector<string> v2(24, "a"); 8 auto it = v1.begin();//此時it指向v1的第一個元素,是f 9 cout << &it << endl;//0x6afeb8 10 11 swap(v1, v2); 12 13 cout << *it << endl;//f 14 cout << &it << endl;//0x6afeb8 15 16 for(const auto indx : v1){//輸出24個a 17 cout << indx << endl; 18 } 19 cout << endl; 20 for(const auto indx : v2){//輸出10個f 21 cout << indx << endl; 22 } 23 return 0; 24 }View Code
除 array 外,swap 不會對任何元素進行拷貝、刪除或插入操作,因此可以保證在常數時間內完成。元素不會被移動也意味著,除 string 外,指向容器的叠代器、引用和指針在 swap 操作之後都不會失效。它們任指向 swap 操作之前所指向的那些元素,但是這些元素已經不屬於原來的容器了。
insert:
註意:只有 vector、deque、list、string 支持 insert 成員
1 #include <iostream> 2 #include <list> 3 #include <vector> 4 using namespace std; 5 6 int main(void){ 7 vector<string> a; 8 list<string> b; 9 10 //在指定位置之前插入某個元素 11 b.insert(b.begin(), "hello");//等價於b.push_front("hello"); 12 a.insert(a.begin(), "hello");//vector不支持push_front,但我們通過insert達到了同樣的效果 13 14 //在指定位置之前插入範圍內元素 15 vector<string> v = {"kjf", "fksl", "fjsl", "fls"}; 16 b.insert(b.begin(), v.end() - 2, v.end());//將v中最後兩個元素插入到b的開始位置 17 b.insert(b.end(), {"fjksl", "fskl", "fsl"});//在b的末尾插入一個列表 18 19 list<string> c; 20 auto it = c.begin(); 21 string s; 22 while(cin >> s){ 23 it = c.insert(it, s);//返回插入元素的叠代器 24 } 25 return 0; 26 }View Code
需要註意的是:向一個 vector、string 或 deque 插入元素會使原來的叠代器、引用和指針失效。
emplace:
1 #include <iostream> 2 #include <vector> 3 using namespace std; 4 5 class gel{ 6 friend ostream& operator<<(ostream&, const gel&); 7 8 private: 9 int x, y, z; 10 11 public: 12 gel(int a, int b, int c) : x(a), y(b), z(c){} 13 ~gel(){} 14 }; 15 16 ostream& operator<<(ostream &os, const gel &it){ 17 os << it.x << " " << it.y << " " << it.z; 18 return os; 19 } 20 21 int main(void){ 22 vector<gel> a; 23 a.emplace_back(1, 2, 3);//隱式使用了構造函數 24 // a.push_back(1, 2, 3);//錯誤,沒有接受3個參數的push_back版本 25 a.push_back(gel(2, 3, 4));//創建一個臨時的gel對象傳遞給push_back 26 27 // a.emplace_back();//錯誤,gel類沒有默認構造函數 28 a.emplace(a.begin(), 1, 2, 3);//將gel(1, 2, 3)插入到a的首元素前 29 30 for(const auto indx : a){ 31 cout << indx << endl; 32 } 33 return 0; 34 }View Code
c++11標準引入了三個新成員——emplace_front、emplace 和 emplace_back,這些操作構造而不是拷貝元素。這些操作分別對應 push_back、insert 和 push_back。
註意:emplace 函數在容器中直接構造元素。傳遞給 emplace 函數的參數必須與元素類型的構造函數匹配。
順序容器2