順序容器
1 順序容器概述
名稱 | 描述 |
---|---|
vector |
可變大小數組,支持快速隨機訪問。在尾部之外的位置插入元素或刪除元素可能很慢 |
deque |
雙端隊列,支持快速隨機訪問。從頭尾位置插入/刪除速度很快 |
list |
雙向鏈表,只支持雙向順序訪問。在list中任何位置進行插入和刪除操作速度都很快 |
forward_list |
單向鏈表,只支持單向順序訪問。在任何位置進行插入和刪除操作都很快 |
array |
給定大小數組。支持快速隨機訪問。不能添加或刪除元素 |
string |
與vector 相似的容器,隨機訪問快,在尾部插入/刪除速度快 |
Tips:
- string和vector均是將元素保存在連續的內存空間中,由於儲存空間的連續,因此利用下標計算其地址非常快。但在容器的中間位置添加或刪除元素會非常耗時:在一次插入操作後,需要移動插入或刪除位置之後的所有元素,而且添加一個元素後可能還需要分配額外的儲存空間。
- list和forward_list優點是在容器如何位置添加和刪除操作都很快。但這兩類容器不支持隨機訪問。訪問一個元素時,只能遍歷整個容器。額外內存開銷比其他容器較大。
- deque支持快速隨機訪問,在中間位置添加和刪除元素的代價搞,但是在兩端添加或刪除元素都很快
選擇容器的基本原則:
- 一般情況下,優先選擇
vector
- 若程序有很多小的元素,且空間的額外開銷很重要,則不要使用
list
或forward_list
- 若程序要求隨機訪問元素,應使用
vector
或deque
- 若程序要求在容器的中間插入或刪除元素,應使用
list
或forward_list
- 若程序要求在容器的頭尾位置插入或刪除元素,但不會在中間位置進行插入或刪除,則使用deque
- 若程序只有在讀取輸入時才需要在容器中間位置插入元素,隨後需要隨機訪問元素。如果必須在中間位置插入元素,則在輸入階段使用list,輸入完成之後,將list的內容拷貝到一個
vector
中。
2 容器庫概覽
類型別名 | 描述 |
---|---|
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 ...}; |
列表初始化c |
大小
名稱 | 描述 |
---|---|
c.size() |
c中元素的數目(不支持forward_list) |
c.max_size() |
c可保存的最大元素數目 |
c.empty() |
若c中儲存了元素,返回false,否則返回true |
添加/刪除元素(不適用與array)
名稱 | 描述 |
---|---|
c.insert(args) |
將args 中的元素拷貝進c |
c.emplace(args) |
使用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 |
2.1 容器類型成員
示例如下:
//iter是通過list<stirng>定義的一個迭代器類型
list<string>::iterator iter;
//count是通過vector<int>定義的一個difference_type類型
vector<int>::difference_type count;
//
2.2 容器定義和初始化
將一個新容器創建爲另外一個容器的拷貝的方法有兩種:可以直接拷貝整個容器,或者(array除外)拷貝由一個迭代器對指定的元素範圍。當將一個容器初始化爲另外一個容器的拷貝時,兩個容器的容器類型和元素類型都必須相同。
示例:
//拷貝元素,直到(但不包括)it指向的元素
deque<string> authList(authors.begin(), it);
2.3 賦值和swap
操作 | 描述 |
---|---|
c1 = c2 |
將c1 中的元素替換爲c2 中元素的拷貝 |
c = {a,b,c...} |
將c1 中的元素替換爲初始化列表中元素的拷貝(array不適用) |
swap(c1, c2) |
交換c1 和c2 中的元素,兩者必須具有相同類型,比拷貝快 |
c1.swap(c2) |
同上 |
assign操作不適用於關聯容器和array
操作 | 描述 |
---|---|
seq.assign(b, e) |
將seq中的元素替換爲迭代器b和e所表示的範圍中的元素,b和e不能指向seq中的元素 |
seq.assign(il) |
將seq中的元素替換爲初始化列表il 中的元素 |
seq.assign(n, t) |
將seq中的元素替換爲n個值爲t的元素 |
Tips:賦值相關運算會導致指向左邊容器內部的迭代器,引用和指針失效。而swap操作將容器內容交換不會導致指向容器的迭代器,引用和指針失效(容器類型爲array和string的情況除外)
賦值運算要求左邊和右邊的運算對象具有相同的類型,assign操作用參數指定的元素(的拷貝)替換左邊容器中的所有元素。例如可以將一個vector中的一段char*
值賦給一個list中的string:
list<string> names;
vector<const char*> oldstyle;
names.assign(oldstyle.cbegin(),oldstyle.cbegin());
使用swap操作用來交換兩個相同類型容器的內容。除array外,交換兩個容器內容的操作中,元素本身未交換,swap只是交換了兩個容器的內部數據結構。這意味着元素不會被移動,因此除string外,指向容器的迭代器、引用和指針在swap操作之後都不會失效。而swap兩個array會真正交換兩者的元素,因此對於array,在swap操作之後,指針、引用和迭代器所綁定的元素保持不變,但元素值已經和另外一個array中對應的元素的值進行了交換。
2.3 容器大小操作
成員函數size返回容器中元素的數目;empty當size爲0時返回布爾值true,否則返回false;max_size返回一個大於或等於改類型容器所能容納的最大元素的值。
2.4 關係運算符
關係運算符左右兩邊的運算對象必須是相同類型的容器,且必須保存相同類型的元素。比較方式如下:
- 如果兩個容器具有相同的大小且所有的容器都兩兩對應相等,則這兩個容器相等;否則兩個容器不等。
- 如果兩個容器大小不同,但較小的容器中每個元素都等於較大容器中的對應元素,則較小容器小於較大容器。
- 如果兩個容器都不是另外一個容器的前綴子序列,則比較的結果取決於第一個不相等的元素的比較結果
3 順序容器操作
3.1 向順序容器添加元素
操作 | 描述 |
---|---|
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.insert(p, n, t) |
在迭代器p指向的元素之前插入n個值爲t的元素,若n爲0,則返回p;否則返回指向新添加的第一個元素的迭代器 |
c.insert(p, b, e) |
將迭代器b和e指定範圍內的元素插入到迭代器p指向的元素之前,b和e之間不能指向e。若範圍爲空,則返回p;否則返回指向新添加的第一個元素的迭代器 |
c.insert(p, il) |
il是一個花括號包圍的元素值列表。 |
Tip:這些操作會改變容器的大小,array不支持這些操作;forward_list有自己專用版本的insert和emplace;forward_list不支持push_back和emplace_back;vector和string不支持push_front和emplace_front;
使用insert
的返回值,可以在容器中一個特定位置反覆插入元素,示例:
list<string> lst;
auto iter = lst.begin();
while(cin >> word)
{
iter = lst.insert(iter, word); //等價調用push_front
}
使用emplace
操作,是將參數傳遞給元素類型的構造函數。當調用push或insert成員時,是將元素類型的對象傳遞給他們,這些對象是拷貝到容器中;當調用emplace成員函數時,則是將參數傳遞給元素類型的構造函數,emplace成員使用這些參數在容器管理的內存空間中直接構造元素。示例:
class Book
{
public:
Book(string n = "",int i = 0)
:name(n),price(i)
{
}
private:
string name;
int price;
};
vector<Book> c;
c.emplace_back("111",123);
c.push_back(Book("111",123));
3.2 訪問元素
操作 | 描述 |
---|---|
c.back() |
返回c中尾元素的引用。若c爲空,函數行爲未定義 |
c.front() |
返回c中首元素的引用。若c爲空,函數行爲未定義 |
c[n] |
返回c中下標爲n的元素的引用,若n>c.size(),則函數行爲未定義 |
c.at(n) |
返回c中下標爲n的元素的引用,若如果下標越界,則拋出異常:out_of_range |
Tip:at和下標操作只適用於string、vector、deque、array
back不是適用於forward_list
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 |
Tip:
- forward_list有特殊版本的erase
- forward_list不支持pop_back;vector和string不支持pop_front。
- 刪除deque中除首尾位置之外的任何元素都會使所有迭代器、引用、指針失效
- 指向vector或string中刪除點之後位置的迭代器、引用、指針失效
3.4 特殊的forward_list操作
forward_list是單向鏈表,由於在單向鏈表中,沒有簡單的方法來獲取一個元素的前驅,因此在一個forward_list中添加或刪除元素的操作是通過改變給定元素之後的元素來完成的。與其他容器的操作不同,forward_list定義了名爲insert_after、emplace_after和erase_after的操作。
操作 | 描述 |
---|---|
lst.before_begin() |
返回指向鏈表首元素之前不存在的元素的迭代器,此迭代器不能解引用 |
lst.cbefore_begin() |
cbefore_begin() 返回一個const_iterator |
lst.insert_after(p, t) |
在迭代器p之後的位置插入元素,t是一個對象 |
lst.insert_after(p, n, t) |
n表示數量 |
lst.insert_after(p, b, e) |
b和e表示範圍的一對迭代器 |
lst.insert_after(p, il) |
il表示一個花括號列表 |
emplace_insert(p, args) |
使用agrs 在p指定的位置之後創建一個元素。返回一個指向這個新元素的迭代器 |
lst.erase_after(p) |
刪除p指向的位置之後的元素 |
lst.erase_after(b, e) |
刪除b之後直到e之間的元素,返回一個指向被刪除元素之後元素的迭代器 |
3.5 改變容器大小
操作 | 描述 |
---|---|
c.resize(n) |
調整c的大小爲n個元素 |
c.resize(n, t) |
調整c的大小爲n個元素。任何新添加的元素都初始化爲值t |
resize不適用與array,如果resize縮小容器,則指向被刪除元素的迭代器、引用和指針都會失效;
對vector、string或deque進行resize操作可能導致迭代器、指針和引用失效。
3.5 容器操作可能使迭代器失效
向容器添加元素後:
- 如果容器是vector或string,且儲存空間被重新分配,則指向容器的迭代器、指針和引用都會失效。如果空間未出現分配,指向插入位置之前的元素的迭代器、指針和引用仍然有效,但在該位置之後的元素的迭代器、指針和引用將會失效
- 對於deque,插入到除首尾之外的任何位置都會導致迭代器、指針和引用失效。如果在首尾位置添加元素,迭代器會失效,但指向存在的元素的引用和指針不會失效
- 對於list和forward_list,指向容器的迭代器、指針和引用仍然有效。
在容器中刪除元素後:
- 對於list和forward_list,指向容器其他位置的迭代器、引用指針仍然有效
- 對於deque,若在首尾之外的任何位置刪除元素,那麼指向被刪除元素之外其他元素的迭代器、指針或引用會失效;若刪除首元素,則不受影響;若刪除尾元素,則尾後迭代器失效,其他不受影響
- 對於vector或string,指向被刪除元素之前元素的迭代器、引用和指針仍然有效
4 vector對象是如何增長的
4.1 管理容量的成員函數
操作 | 描述 |
---|---|
c.shrink_to_fit() |
將capacity() 減少爲與size()相同大小 |
c.capacity() |
不重新分配內存空間的話,c可以保存多少元素 |
c.reserve(n) |
分配至少能容納n個元素的內存空間 |
reserve並不改變容器中元素的數量,僅影響vector預先分配多大的內存空間。
只要沒有操作需求超出vector的容量,vector就不能重新分配內存空間。只有當在執行insert操作時,size與capacity相等,或者調用resize或reserve 時給定的大小超過當前capacity,vector纔可能重新分配內存空間,分配多少取決於具體實現。==如在需要分配新內存空間時將當前容量翻倍,此時可以調用shrink_to_fit來要求vector將超出當前大小的多餘內存退回給系統。==詳細說明將另外一篇文章vector對象增長說明
5 額外的string操作
5.1 構造string的其他方法
操作 | 描述 |
---|---|
string s(cp, n) |
s是cp 指向的數組前n個字符的拷貝 |
string s(s2, pos2) |
s是string s2 從下標pos2 開始的字符的拷貝,若pos2>s2.size() ,構造函數的行爲未定義 |
string s(s2, pos2, len2) |
s是string s2 從下標pos2 開始len2 個字符的拷貝,若pos2>s2.size() ,構造函數的行爲未定義 |
substr
操作
示例如下:
string s("hello world");
string s2 = s.substr(0, 5); //s2 = hello
string s3 = s.substr(6); //s2 = world
string s4 = s.substr(6, 11); //s2 = world
string s5 = s.substr(12); //拋出一個out_of_range異常
string類還定義了兩個額外的成員函數:append和replace。append操作是在string末尾進行插入操作的一種簡寫形式。
s2.apppend("4th ed"); //等價方法:將"4th ed"追加到s2
5.2 string搜索操作
操作 | 描述 |
---|---|
s.find(args) |
查找s中args 第一次出現的位置 |
s.rfind(args) |
查找s中args 最後一次出現的位置 |
s.find_first_of(args) |
在s中查找args 中任何一個字符第一次出現的位置 |
s.find_last_of(args) |
在s中查找args 中任何一個字符最後一次出現的位置 |
s.find_first_not_of(args) |
在s中查找第一個不在args 中的字符 |
s.find_last_not_of(args) |
在s中查找最後一個不在args 中的字符 |
args
必須是以下形式之一
形式 | 描述 |
---|---|
c, pos |
從s中位置pos 開始查找字符c。pos 默認爲0 |
s2, pos |
從s中位置pos 開始查找字符串s2 。pos 默認爲0 |
cp, pos |
從s中位置pos 開始查找指針cp 指向的以空字符結尾的C風格字符串。pos 默認爲0 |
cp, pos, n |
從s中位置pos 開始查找指針cp 指向的數組的前n個字符。pos 和n無默認值 |
5.3 compare函數和數值轉換
參數形式 | 描述 |
---|---|
s2 |
比較s 和s2 |
pos1, n1, s2 |
將s中從pos1 開始的n1 個字符與s2 進行比較 |
pos1, n1, s2, pos2, n2 |
將s 中從pos1 開始的n1 個字符與s2 中從pos2 開始的n2 個字符進行比較 |
cp |
比較s 與cp 指向的以空字符結尾的字符數組 |
pos1, n1, cp |
將s中從pos1 開始的n1 個字符與cp 指向的以空字符結尾的字符數組進行比較 |
pos1, n1, cp, n2 |
將s中從pos1 開始的n1 個字符與指針cp 指向的地址開始的n2 個字符進行比較 |
操作 | 描述 |
---|---|
to_string(val) |
返回數值val的string表示,val可以是任何算術類型 |
stoi(s, p, b) stol(s, p, b) stoul(s, p, b) |
返回s的起始子串(表示整數內容)的數值,返回類型爲int、long、 |
stoll(s, p, b) stoull(s, p, b) |
unsigned long、long long、unsigned long long。b表示轉換用的基數,默認爲10,p是size_t指針,用來保存s中第一個非數值字符的下標 |
stof(s, p) stod(s, p) stold(s, p) |
返回s的起始子串(表示浮點數內容)的數值,返回類型分別是float、double、long double |
6 容器適配器
標準庫定義了三個順序容器適配器:stack、queue和priority_queue。
操作 | 描述 |
---|---|
size_type |
一種類型,足以保存當前類型的最大對象的大小 |
value_type |
元素類型 |
container_type |
實現適配器的底層容器類型 |
A a; |
創建一個名爲a的空適配器 |
A a(c); |
創建一個名爲a的適配器,帶有容器c的一個拷貝 |
關係運算符 |
每個適配器都支持所有的關係運算符:==、!=、<、<=、>、>= |
a.empty() |
若a包含任何元素,返回false,否則返回true |
a.size() |
返回a中的元素數目 |
swap(a, b) a.swap(b)) |
交換a和b的內容,a和b必須有相同類型,包括底層容器類型也必須相同 |
定義一個適配器:默認構造函數創建一個空對象,接受一個容器的構造函數拷貝改容器來初始化適配器。假定deq是一個deque<int>
示例如下:
stack<int> stk(deq); //從deq拷貝元素到stk
在構造適配器時,均要求容器具有添加、刪除以及訪問尾元素的能力。同時要注意以下幾點:
- stack只要求push_back,pop_back和back,因此可以使用除array和forward_list之外的任何容器類型來構造stack
- queue要求back、push_back,front和push_front,因此可以構造與list或deque之上,但不能基於vector構造。
- priority_queue除了front、push_back和pop_back操作之外,還要求隨機訪問能力,因此可以構造於vector或deque之上,但不能基於list構造。
棧適配器
stack類型定義在stack頭文件中,下面的示例展示瞭如何使用stack:
stack<int> intStack; //空棧
//填滿棧
for (size_t ix = 0;ix != 10; ++ix )
{
intStack.push(ix); //intStack保存0到9十個數
}
while ( !intStack.empty()) //intStack中有值就繼續循環
{
int value = intStack.top();
//使用棧頂值的代碼
intStack.pop(); //彈出棧頂元素,繼續循環
}
操作 | 描述 |
---|---|
s.pop() |
刪除棧頂元素,但不返回該元素之 |
s.push(item) |
創建一個新元素壓入棧頂,該元素通過拷貝或移動item而來 |
s.emplace(args) |
或者由args 構造 |
s.top() |
返回棧頂元素,但不將元素彈出棧 |
棧默認基於deque實現,也可以再list或vector之上實現
隊列適配器
queue和priority_queue適配器定義在queue,以下是其支持的操作
操作 | 描述 |
---|---|
q.pop() |
返回queue的首元素或priority_queue的最高優先級的元素 |
q.front() |
返回首元素或尾元素,但不刪除此元素 |
q.back() |
只適用於queue |
q.top() |
返回最高優先級元素,但不刪除該元素(只適用於priority_queue) |
q.push(item) |
在queue末尾或priority_queue中恰當的位置創建一個元素, |
q.emplace(agrs) |
其值爲item,或者由agrs 構造 |
queue默認基於deque實現,priority_queue默認基於vector實現;
queue也可以用list或vector實現,priority_queue也可以用deque實現;