1. 程式人生 > >順序容器2

順序容器2

限制 分享 code sin empty 技術 esp lis mes

順序容器類型:

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