C++Primer第五版【筆記】——第九章——順序容器
阿新 • • 發佈:2019-02-12
1. 順序容器概述
表一中的順序容器,提供了對元素快速的順序訪問。但是其他操作的開銷則不同:- 新增或刪除元素的開銷
- 進行非順序訪問的開銷
vector | 可變長度陣列。支援快速隨機訪問。在非尾部插入或刪除元素速度很慢 |
deque | 雙端佇列。支援快速隨機訪問。快速在頭或尾的插入和刪除 |
list | 雙向連結串列。只支援雙向的順序訪問。可以在list的任何位置快速插入和刪除 |
forward_list | 單向連結串列。只支援單向順序訪問。可在任何位置快速插入和刪除 |
array | 定長陣列。支援快速隨機訪問。不能新增或刪除元素。 |
string | 專門用來儲存字元的容器,類似vector。支援快速隨機訪問。快速在尾部的 插入和刪除。 |
如何選擇使用哪個順序容器?
- 使用vector,除非你有理由使用其他容器
- 如果你的程式有許多小元素,且空間開銷很重要時,不要使用list或forward_list
- 如果程式需要隨機訪問元素,使用vector或deque
- 如果程式需要在容器中間插入或刪除元素,使用list或forward_list
- 如果程式需要在頭部或尾部插入或刪除元素,而不是中間,使用deque
- 如果程式只在讀入資料時需要在容器中間插入元素,隨後需要隨機訪問元素:
-- 首先,確定是否真的需要在容器中間插入元素。一般先為vector追加元素,然後呼叫sort函式排序會更容易。
-- 如果真的需要在中間插入元素,考慮在輸入階段使用list。然後當輸入完成後,將list複製到vector。
2. 容器庫概述
容器的操作種類可以劃分為一個層次:- 一些操作是通用的。
Type Aliases iterator 容器類的迭代器型別 const_iterator 該迭代器只能讀,不能改變元素 size_type 無符號整型,足夠儲存最大可能的容器類的大小 difference_type 有符號整型,足夠儲存兩個迭代器的距離 value_type 元素型別 reference 元素左值型別;與value_type&等價 const_reference 元素的const左值型別(const value_type &) Construction C c; 預設建構函式,空容器 C c1(c2); 建立c1為c2的一個副本 C c(b, e); 複製迭代器b和e範圍內的元素(不適用於array) C c{a, b, c...}; 列表初始化 Assignment and swap c1 = c2 用c2中的元素值代替c1中的元素 c1 = {a,b,c...} 用列表中的元素代替c1中的元素 (不適用於array) a.swap(b) 交換a和b中的元素 swap(a, b) 等價於a.swap(b) Size c.size() c中元素的個數(不適用於forward_list) c.max_size() c最多可以儲存的元素個數 c.empty() 如果c為空,true Add/Remove Elements
(not valid for array)Note: the interface to these operations varies by container type c.insert(args) 將args指代的元素插入c中 c.emplace(inits) 使用inits構造一個c中的元素 c.erase(args) 刪除args指代的元素 c.clear() 刪除c中所有元素,返回void Equality and Relational Operators ==, != 所有容器都支援 <, <=, >, >= 關係(非有序關係容器不支援) Obtain Iterators c.begin(), c.end() Return iterator to the first, one past the last element in c c.cbegin(), c.cend() Return const_iterator Additional Members of Reversible Containers (not valid for forward_list) reverse_iterator Iterator that addresses elements in reverse order const_reverse_iterator Reverse iterator that cannot write the elements c.rbegin(), c.rend() Return iterator to the last, one past the first element in c c.crbegin(), c.crend() Return const_reverse_iterator - 順序容器,關係容器,非順序容器的其他操作
Defining and Initializing Containers C c; 預設建構函式。如果C是array,則c中的元素是預設初始化,否則c為空 C c1(c2) c1 is a copy of c2. c1 and c2 must have the same type
(必須是相同型別的容器,且其中的元素型別也相同;對於array,長度也需要相同)C c{a,b,c...} c is a copy of the elements in the initializer list.列表中元素的型別必須與C中元素的型別是相容的。對於array,列表中元素的個數必須小於或等於array的長度,缺少的部分用值初始化 C c={a,b,c...} C c(b,e) c is a copy of the elements in the range denoted by iterators b and e. 元素型別必須和C的元素型別相容。(不適用於array) Constructors that take a size are valid for
sequential containers only(not including array) C seq(n) seq has n value-initialized elements; the constructor is explicit.(Not valid for string). C seq(n, t) seq has n elements with value t.
vector<vector<int>> vvi;
vector<vector<int> > vv_i; // 舊版本需要加一個空格
2.1 迭代器
迭代器支援的操作和指標類似,包括自增、自減、解引用等。對於不同的容器,其迭代器支援的操作也會不同。比如forward_list就不支援--操作。 需要特別指出的是,迭代器的範圍是一個前開後閉區間:[begin, end)2.2 容器的型別成員
容器中定義了一些型別成員,比如size_type,iterator, const_iterator等。這些類型別名可以讓我們在使用的時候不需要知道具體型別,在泛型程式設計中很有用。2.3 begin和end
【C++11】 c版本的begin和end是新標準加入的,使用auto可以很方便的定義。vector<string> vs;
const vector<string> c_vs;
auto beg = vs.begin(); // iterator
auto cbeg = c_vs.begin(); // const_iterator
auto cbeg1 = vs.cbegin(); // const_iterator
不帶c的版本在內部是過載函式。一個const版本的和一個非const版本的。如果是const物件,則呼叫const版本,如果是非const物件則呼叫非const版本。
2.4 定義和初始化容器
除了array之外,每個容器都定義了預設建構函式,以及一個指定容器大小和初始化元素值的建構函式。 根據一個容器來初始化另一個容器有兩種方法:第一種是直接複製,容器型別和元素型別必須匹配;第二種是用迭代器指定一個範圍,容器型別和元素型別都不需要一致,元素型別要保證是可以轉換的。vector<string> vs = {"hello", "world"};
list<const char*> lcc = {"ok", "I", "want"};
deque<string> ds(vs); // error
forward_list<string> fs(lcc.begin(), lcc.end()); // ok
順序容器的初始化:
vector<int> vi(10, 1024); // 初始化為10個1024
list<string> ls(10, "ok"); // 初始化為10個"ok"
forward_list<int> fi(10); // 初始化為10個0
deque<string> ds(10); // 初始化為10個空string
需要指出的是,只提供一個元素個數的建構函式,需要元素本身(如果是類型別)具有預設建構函式。
array
array和內建陣列型別類似,固定長度,陣列大小是型別的一部分。array<int, 50> a = {0, 1, 2 ,3}; // 其餘元素為0
2.5 賦值和交換
assign operations not valid for associative containers or array | |
---|---|
seq.assign(b,e) | Replaces elements in seq with those in the range denoted by iterators b and e. The iterators b and e must not refer to elements in seq. |
seq.assign(il) | Replaces the elements in seq with those in the initializer list il. |
seq.assign(n,t) | Replaces the elements in seq with n elements with value t. |
與內建陣列不同的是,array支援賦值。
array<int,5> arr1 = {1,2,3,4,5};
array<int,5> arr2 = {0};
arr1 = arr2;
【注】賦值操作的等號左右兩邊的型別必須一致
使用assign
順序容器還支援assign操作,提供了更加靈活的賦值,只要型別是相容的(即可以相互轉換)。list<string> ls;
vector<const char*> vcc;
ls = vcc; // error
ls.assign(vcc.cbegin(), vcc.cend()); // ok
使用swap
swap可以用來交換兩個相同型別的容器,該操作可以在常數時間內完成。資料本身沒有交換,交換的是內部資料結構,比如迭代器,引用,指標。2.6 size操作
size函式返回當前容器中包含的元素個數;如果容器為空,empty返回ture,否則返回false;max_size返回容器最大可以容納的元素個數。2.7 關係操作符
所以容器都支援相等操作(==和!=);所以容器除了無序關聯容器,都支援關係操作(>, >=, <, <=)。 【注】只有容器的元素支援相應的關係操作時,才能比較該容器。3. 順序容器操作
3.1 新增元素
除了array,所以的庫容器都支援容器大小在執行時的動態改變。c.push_back(t) c.emplace_back(args) |
在容器c的尾部,建立一個值為t的元素,或根據args構建一個元素。返回void |
c.push_front(t) c.emplace_front(args) |
在容器c的頭部,建立一個值為t的元素,或根據args構建一個元素。返回void |
c.insert(p,t) c.emplace(p,args) |
在迭代器p所指元素的前面,插入一個值為t的元素,或根據args構建一個元素; 返回指向插入元素的迭代器 |
c.inesrt(p, n, t) | 在迭代器p所指元素的前面,插入n個值為t的元素;返回指向插入的第一個元素的迭代器, 如果n為0,返回p |
c.insert(p, b, e) | 在迭代器p所指元素的前面,插入迭代器指向範圍為[b,e)的元素,b,e不能指向c本身。 返回指向插入的第一個元素的迭代器,如果範圍為空,返回p |
c.insert(p, il) | il是包含元素值的括號序列。在迭代器p指向的元素前面插入該序列。返回指向插入的 第一個元素的迭代器,如果序列為空,返回p |
list<string> ls;
vector<string> vs = {"this", "is", "for", "test"};
ls.insert(ls.begin(), vs.end()-2, v.end());
ls.insert(ls.end(), {"love","coding"});
【c++11】新標準中,接受一個計數引數或迭代器範圍的insert版本會返回指向插入的第一個元素的迭代器(之前的版本返回void)。 【c++11】
emplace_front, emplace, emplace_back是新標準加入的三個操作,與push_front, insert, push_back不同的是,它們用args引數呼叫元素型別的建構函式在插入的位置建立新的元素,而不是根據傳遞的元素複製一個副本。 相當於根據引數型別,呼叫相應的建構函式,在容器所在的記憶體空間(插入的位置)建立新的元素。
3.2 訪問元素
c.back() | 返回容器c的最後一個元素的引用。如果c為空,則結果未定義 |
c.front() | 返回容器c的第一個元素的引用。如果c為空,則結果未定義 |
c[n] | 返回下標n所在位置的元素。如果n>=c.size(),則結果為定義 |
c.at(n) | 返回下標n所在位置的元素。如果n超出範圍,則丟擲out_of_range 異常 |
3.3 刪除元素
c.pop_back() | 刪除容器c的最後一個元素。如果c為空,則未定義。返回void |
c.pop_front() | 刪除容器c的第一個元素。如果c為空,則未定義。返回void |
c.erase(p) | 刪除迭代器p指向的元素,返回指向被刪除元素後一位的迭代器。 |
c.erase(b,e) | 刪除迭代器範圍為[b,e)的元素,返回指向被刪除元素後一位的迭代器。 |
c.clear() | 刪除c的所有元素,返回void |
3.4 forward_list的專用操作
對於單向連結串列來說,插入或刪除元素,會改變其前面一個元素中的指標。所以它的插入和刪除操作不同於其他容器。 lst.before_begin() lst.cbefore_begin() |
指向連結串列開頭前一個位置的迭代器,該迭代器指向的不是連結串列中的某個元素, 不能解引用。cbefore_begin()返回const_iterator |
lst.insert_after(p,t) lst.insert_after(p,n,t) lst.insert_after(p,b,e) lst.insert_after(p,il) |
在迭代器p所指元素的後面插入相應的元素。 |
emplace_after(p, args) | 在迭代器p所指元素的後面,根據args構造一個新的元素 |
lst.erase_after(p) lst.erase_after(b,e) |
刪除在迭代器p所指的,或範圍為[b,e)的元素。返回被刪除元素的後繼的迭代器。 |
3.5 改變容器大小
容器(除了array)可以使用resize來改變大小。c.resize(n) | 重新定義c的大小為n。如果n < c.size(),刪除多餘的元素。 否則新增新的元素(值初始化) |
c.resize(n,t) | 重新定義c的大小為n。增加的新元素值為t |
4. vector如何增長
vector的記憶體管理是,根據一定的策略事先分配一段記憶體空間,當元素個數增長到空間不夠用時,再分配新的記憶體空間,然後將元素搬到新的空間,再釋放舊的空間。c.shrink_to_fit() | 將capacity()縮小到與size()相等 |
c.capacity() | 容器c的容量 |
c.reserve(n) | 使c的容量至少可以儲存n個元素 |
#include <iostream>
#include <vector>
using namespace std;
int main()
{
vector<int> vi;
cout << "size: " << vi.size() << "; capacity: " << vi.capacity() << endl;
for(vector<int>::size_type idx = 0; idx <= 50; ++idx) {
vi.push_back(idx);
cout << "size: " << vi.size() << "; capacity: " << vi.capacity() << endl;
}
return 0;
}
5. string操作
略過。6. 容器介面卡
介面卡是一個通用概念。包括容器介面卡,迭代器介面卡和函式介面卡。介面卡是一種使一種物件按另一種物件的規則操作的機制。 除了前面幾節講到的順序容器,C++庫還定義了三種順序容器介面卡:stack, queue和priority_queue。定義一個介面卡
預設情況下,stack和queue在deque上實現,priority_queue在vector上實現。stack<int> stk;
也可以指定用來實現的容器:
stack<int, vector<int>> stk_vec
實現介面卡的容器是有限制的。所有的介面卡都要求有新增和刪除元素的能力。所以array不能使用。所以的介面卡都要求在容器的末尾新增和刪除元素,forward_list也不能使用。stack要求push_back, pop_back和back操作,可以用vector, deque, list實現。queue要求back, push_back, front和push_front操作,可以用list, deque實現。priority_queue除了要求front, push_back和pop_back
操作,還要求隨機訪問,所以可以用vector 或 deque實現。