1. 程式人生 > >順序容器----順序容器操作,vector物件如何增長,額外的string操作,容器介面卡

順序容器----順序容器操作,vector物件如何增長,額外的string操作,容器介面卡

 

一、順序容器操作

1、向順序容器新增元素

  向順序容器(array除外)新增元素的操作:

操作 說明
c.push_back(t) 在c的尾部建立一個值為t的元素。返回void
c.emplace_back(args) 在c的尾部建立一個由args建立的元素。返回void
c.push_front(t) 在c的頭部建立一個值為t的元素。返回void
c.emplace_front(args) 在c的頭部建立一個由args建立的元素。返回void
c.insert(p, t) 在迭代器p指向的元素之前建立一個值為t的元素。返回指向新新增的元素的迭代器
c.emplace(p, args) 在迭代器p指向的元素之前建立一個由args建立的元素。返回指向新新增的元素的迭代器
c.insert(p, n, t) 在迭代器p指向的元素之前插入n個值為t的元素。返回指向新新增的第一個元素的迭代器。若n為0,返回p
c.insert(p, b, e) 將迭代器b和e指定的範圍內的元素插入到迭代器p指向的元素之前。b和e不能指向c中的元素。返回指向新新增的第一個元素的迭代器;若範圍為空,返回p
c.insert(p, items) items是一個花括號包圍的元素值列表。將這些給定值插入到迭代器p指向的元素之前。返回指向新新增的第一個元素的迭代器。若列表為空,則返回p

forward_list有自己專有版本的insert和emplace。forward_list不支援push_back和emplace_back。vector和string不支援push_front和emplace_front。

  向一個vector、string或deque插入元素會使所有指向容器的迭代器、引用和指標失效

  當我們使用這些操作時,必須記得不同容器使用不同的策略來分配元素空間,而這些策略直接影響效能。在一個vector或string的尾部之外的任何位置,或是一個deque的首尾之外的任何位置新增元素,都需要移動元素。向一個vector或string新增元素可能引起整個物件儲存空間的重新分配。重新分配一個物件的儲存空間需要分配新的記憶體,並將元素從舊的空間移動到新的空間中。

1)使用push_back

  push_back將一個元素追加到容器的尾部。除array和forward_list之外,每個順序容器都支援push_back。

  當我們用一個物件來初始化容器時,或是將一個物件插入到容器中時,實際上放入到容器中的是物件值的一個拷貝,而不是物件本身

2)使用push_front

  forward_list將一個元素插入到元素的頭部。list、forward_list和deque支援push_front。

3)使用insert

  insert允許我們在容器中任意位置插入0個或多個元素。vector、deque、list和string都支援insert。

4)使用emplace

  新標準引入了三個新成員----emplace_front、emplace、emplace_back,這些操作構造而不是拷貝元素。這些操作分別對應push_front、insert、push_back,允許我們將元素放置在容器頭部,一個指定位置之前或容器尾部。

  當我們呼叫一個emplace成員函式時,是將引數傳遞給元素型別的建構函式。emplace使用這些引數在容器管理的記憶體空間中直接構造元素。傳遞給emplace函式的引數必須與元素型別的建構函式相匹配。

 

2、訪問元素

  在順序容器中訪問元素的操作:

  at和下標操作只適用於string、vector、deque和array。

  back不適用於forward_list。

操作 函式
c.back() 返回c中尾元素的引用。若c為空,函式行為未定義
c.front() 返回c中首元素的引用。若c為空,函式行為未定義
c[n] 返回c中下標為n的元素的引用,n是一個無符號整數,若n>=c.size(),則函式行為未定義
c.at() 返回下標為n的元素的引用。如果下標越界,則丟擲out_of_range異常

注:對一個空容器呼叫front和back是一種嚴重的程式設計錯誤。

1)訪問成員函式返回的是引用

  在容器中訪問元素的成員函式(即front、back、下標和at)返回的都是引用。如果程式時一個const物件,則返回值是const的引用;如果程式不是const的,則返回值是普通引用。

 1 #include <iostream>
 2 #include <string>
 3 #include <vector>
 4 #include <deque>
 5 #include <list>
 6 #include <forward_list>
 7 #include <array>
 8 
 9 int main()
10 {
11     std::vector<int> c = { 1, 2, 3 };
12     if (!c.empty())
13     {
14         c.front() = 4; // 修改第一個元素的值
15         auto &v = c.back();
16         v = 1024; // 修改最後一個元素的值
17         auto v2 = c.back(); // v2是最後一個元素的拷貝
18         v2 = 0; // 最後一個元素未改變
19         for (auto iter = c.begin(); iter != c.end(); ++iter)
20         {
21             std::cout << (*iter) << " ";
22         }
23         std::cout << std::endl;
24     }
25     return 0;
26 }
View Code

 

3、刪除元素

   順序容器的刪除操作:

  這些操作會改變容器的大小,所以不適用於array。

  forward_list有特殊版本額erase。

  forward_list不支援pop_back;vector和string不支援pop_front。

操作 說明
c.pop_back() 刪除c中尾元素。若c為空,則函式行為未定義。函式返回void
c.pop_front() 刪除c中首元素。若c為空,則函式行為未定義。函式返回void
c.erase(p) 刪除迭代器p所指定的元素,返回一個指向被刪除元素之後元素的迭代器,若p指向尾元素,則返回尾後迭代器。若p是尾後迭代器,則函式行為未定義
c.erase(b, e) 刪除迭代器b和e所指定範圍內的元素[b, e)。返回一個指向最後一個被刪除元素之後元素的迭代器(即e),若e本身就是尾後迭代器,則函式也返回尾後迭代器
c.clear() 刪除c中所有元素。返回void

刪除deque中除首尾位置之外的任何元素都會使所有迭代器、引用和指標失效。指向vector和string中刪除點之後位置的迭代器、引用和指標都會失效

 

4、特殊的forward_list操作

  在forward_list中插入或刪除元素的操作:

操作 函式
lst.before_begin() 返回指向連結串列首元素之前不存在的元素的迭代器。此迭代器不能解引用。cbefore_begin()返回一個const_iterator
lst.cbefore_begin()  
lst.insert_after(p, t)

在迭代器p之後的位置插入元素。

t是一個物件,n是數量;

b和e是表示範圍的一對迭代器(b和e都不能指向lst內);

items是一個花括號列表。

返回一個指向最後一個插入元素的迭代器。如果範圍為空,則返回p。若p為尾後迭代器,則函式行為未定義

lst.insert_after(p, n, t)  
lst.insert_after(p, b, e)  
lst.insert_after(p, items)  
lst.emplace_after(p, args) 使用args在p指定的位置之後建立一個元素。返回一個指向這個新元素的迭代器。若p為尾後迭代器,則函式行為未定義
lst.erase_after(p) 刪除p指向的位置之後的元素,或刪除從b之後到e之間的元素(不包含e),若不存在這樣的元素,則返回尾後迭代器。如果p指向lst的尾元素或者是一個尾後迭代器,則函式行為未定義
lst.erase_after(b, e)  

  一個從forward_list中刪除奇數的例子:

 1 #include <iostream>
 2 #include <string>
 3 #include <vector>
 4 #include <deque>
 5 #include <list>
 6 #include <forward_list>
 7 #include <array>
 8 
 9 int main()
10 {
11     std::forward_list<int> lst = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
12     auto prev = lst.before_begin(); // lst的首前元素
13     auto curr = lst.begin(); // lst的首元素
14     while (curr != lst.end())
15     {
16         if (*curr % 2)
17         {
18             curr = lst.erase_after(prev); // 刪除並移動curr
19         } 
20         else
21         {
22             prev = curr;
23             ++curr;
24         }
25     }
26     for (auto iter = lst.begin(); iter != lst.end(); ++iter)
27     {
28         std::cout << *iter << " ";
29     }
30     std::cout << std::endl;
31     return 0;
32 }
View Code

 

5、改變容器大小

  順序容器的大小操作:

  resize不適用於array。

操作 說明
c.resize(n) 調整c的大小為n個元素。若n<c.size(),則多出的元素被丟棄。若必須新增新元素,對新元素進行值初始化
c.resize(n, t) 調整c的大小為n個元素。任何新新增的元素都初始化為值t

如果resize縮小容器,則指向被刪除元素的迭代器、引用和指標都會失效。對vector、string或deque進行resize()可能導致迭代器、指標和引用失效

 

6、容器操作可能使迭代器失效

  在容器中新增元素和從容器中刪除元素的操作可能會使指向容器元素的指標、引用和迭代器失效。

  在向容器新增元素後:

a、如果容器是vector或string,且儲存空間被重新分配,則指向容器的迭代器、指標和引用都會失效。如果儲存空間未重新分配,則指向插入位置之前的元素的迭代器、指標和引用仍有效,但指向插入位置之後元素的迭代器、指標和引用將會失效。

b、對於deque,插入到除首尾位置之外的任何位置都會導致迭代器、指標和引用失效。如果在首尾位置新增元素,迭代器會失效,但指向存在的元素的引用和指標不會失效。

c、對於list和forward_list,指向容器的迭代器(包括尾後迭代器和首前迭代器)、引用和指標仍有效。

  當我們從一個容器中刪除元素後, 指向被刪除元素的迭代器、指標和引用會失效。當我們刪除一個元素後:

a、對於list和forward_list,指向容器其他位置的迭代器(包括尾後迭代器和首前迭代器)、引用和指標仍有效。

b、對於deque,如果在首尾之外的任何位置刪除元素,那麼指向被刪除元素外其他元素的迭代器、引用和指標也會失效。如果刪除deque的尾元素,則尾後迭代器也會失效,但其他迭代器、引用和指標有效;如果刪除首元素,這些也不會受影響。

c、對於vector和string,指向被刪除元素之前的迭代器、引用和指標仍有效。注意:當我們刪除元素時,尾後迭代器總是失效的。

 

二、vector物件是如何增長的

  為了支援快速訪問,vector將元素連續儲存----每個元素緊挨著前一個元素儲存。

  當不得不獲取新的記憶體空間時,vector和string的實現通常會分配比新的空間的需求更大的記憶體空間。容器預留這些空間作為備用,可用來儲存更多的新元素。這樣,就不需要每次新增新元素都重新分配記憶體空間了。只要操作需求沒有超過vector的容量,vector就不會重新分配空間。

1、管理容量的成員函式

  容器大小管理操作:

  shrink_to_fit只適用於vector、string和deque。

  capacity和reserve只適用於vector和string。

操作 說明
c.shrink_to_fit() 請將capacity()減少為與size()相同大小
c.capacity() 不重新分配記憶體空間的話,c可以儲存多少元素
c.reserve(n) 分配至少能容納n個元素的記憶體空間

reserve並不改變容器中元素的數量,它既能影響vector預先分配多大的記憶體空間。

  只有當需要的記憶體空間超過當前容量時,reserve呼叫才會改變vector的容量。如果需求大小大於當前容量,reserve至少分配與需求一樣大的空間(可能更大)。如果需求大小小於當前容量,reserve什麼也不做。

  呼叫該reserve永遠也不會減少容器佔用的記憶體空間。類似的,resize成員函式只改變容器中元素的數目,而不是容器的容量(即使用resize不會減少容器預留的記憶體空間)。

  在新標準庫中,我們可以呼叫shrink_to_fit來要求vector、string、deque退回不需要的記憶體空間。此函式指出我們不再需要多餘的記憶體空間。但是具體的實現可以選擇忽略此請求。也就是說,呼叫shrink_to_fit也並不保證一定退回記憶體空間。

2)capacity和size

  容器的size是指它已經儲存的元素的數目;而capacity則是在不分配新的記憶體空間的前提下它最多可以儲存多少元素。

 

三、額外的string操作

1、構造string的其他方法

  string型別還支援另外三個建構函式:

操作 說明
n、len2和pos2都是無符號值  
string s(p, n) s是p指向的陣列中的前n個字元的拷貝。此陣列至少應該包含n個字元
string s(s2, pos2) s是string s2從下標pos2開始的字元的拷貝。若pos2>s.size(),建構函式的行為未定義
string s(s2, pos2, len2) s是string s2從下標pos2開始len2個字元的拷貝。若pos2>s2.size(),建構函式的行為未定義。不管len2的值是多少,建構函式至多拷貝s2.size()-pos2個字元

1)substr操作

  子字串操作:

操作 說明
s.substr(pos, n) 返回一個string,包含s中從pos開始的n個字元的拷貝。pos的預設值為0。n的預設值為s.size()-ps,即拷貝從pos開始的所有字元

如果pos超過了string的大小,則substr函式丟擲一個out_of_range異常。如果pos+n大於string的大小,則substr只拷貝到string的末尾。

 

2、改變string的其他方法

  string型別除了支援順序容器的賦值運算子以及assign、insert和erase操作外,還定義了額外的insert和erase版本。

  修改string的其他操作:

操作 說明
s.insert(pos, args) 在pos之前插入args指定的字元。pos可以是一個下標或迭代器。接受下標的版本返回一個指向s的引用;接受迭代器的版本返回指向第一個插入字元的迭代器
s.erase(pos, len) 刪除從位置pos開始的len個字元。如果len被省略,則刪除從pos開始至s末尾的所有字元。返回一個指向s的引用 
s.assign(args) 將s中的字元替換為args指定的字元。返回一個指向s的引用 
s.append(args) 將args追加到s。返回一個指向s的引用 
s.replace(range, args) 刪除s中範圍range內的字元,替換為args指定的字元。range或者是一個下標和一個長度,或者是一對指向s的迭代器。返回一個指向s的引用 

args可以是下列形式之一;append和assign可以使用所有形式。

str不能與s相同,迭代器b和e不能指向s。

 
str 字串str
str, pos, len str中從pos開始最多len個字元 
cp, len 從cp指向的字元陣列的前(最多)len個字元 
cp cp指向的以空字元結尾的字元陣列 
n, c n個字元c 
b, e 迭代器b和e指定的範圍內的字元 
初始化列表 花括號包圍的,以逗號分隔的字元列表 

replace和insert所允許的args形式依賴於range和pos是如何指定的:

replace replace insert insert args可以是
(pos, len, args) (b, e, args) (pos, args) (iter, args)  
str
str, pos, len
cp, len
cp
n, c
b2, e2
初始化列表

 

3、string搜尋操作

  string搜尋操作:

  搜尋操作返回指定字元出現的下標,如果未找到則返回npos。

操作 說明
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無預設值

每個搜尋操作都返回一個string::size_type值,表示匹配發生位置的下標。如果搜尋失敗,則返回一個名為string::npos的靜態成員。標準庫將string::npos定義為一個const string::size_type型別,並初始化為值-1.由於npos是一個unsigned型別,此初始值意味著npos等於任何string的最大的可能的大小。

 

4、compare函式

  標準庫string型別還提供了一組compare函式,根據s是等於、大於還是小於引數指定的字串,s.compare()返回0、正數、負數。

  s.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個字元進行比較

 

5、數值轉換

   string和數值之間的轉換:

操作 說明
to_string(val) 一組過載函式,返回數值val的string表示。val可以是任何算術型別。對每個浮點型別和int或更大的整型,都有相應版本的to_string。與往常一樣,小整型會被提升
stoi(s, p, b)

返回s的起始子串(表示子串)的數值,返回型別分別是int、long、unsigned long、long long、unsigned long long。

b表示轉換所用的基數,預設值為10。p是size_t指標,用來儲存s中第一個非數值字元的下標,p預設為0,即函式不儲存下標 

stol(s, p, b)  
stoul(s, p, b)  
stoll(s, p, b)  
stoull(s, p, b)  
stof(s, p) 返回s的起始子串(表示浮點數內容)的數值,返回值型別分別是float、double或long double。引數p的作用與整數轉換函式中一樣 
stod(s, p)  
stold(s, p)  

 

四、容器介面卡

   標準庫定義了三個順序容器介面卡: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和b的內容,a和b具有相同型別,包括底層容器型別也必須相同
a.swap(b)  

1)定義一個介面卡

  預設情況下,stack和queue是基於deque實現的,priority_queue是在vector之上實現的。我們可以在建立一個介面卡時將一個命名的順序容器作為第二個型別引數,來過載預設容器型別。

 1 #include <iostream>
 2 #include <string>
 3 #include <stack>
 4 #include <queue>
 5 
 6 int main()
 7 {
 8     std::deque<int> deq = { 1, 2, 3 };
 9     std::stack<int> stk(deq);
10 
11     std::stack<int, std::vector<int>> stk1; // 在vector上實現的空棧
12     return 0;
13 }
View Code

  對於一個給定的介面卡,可以使用哪些容器時有限制的。所有介面卡都要求容器具有新增和刪除元素的能力。因此,介面卡不能勾鍵在array之上。類似的,我們也不能使用forward_list來構造介面卡,因為所有介面卡都要求容器具有新增、刪除以及訪問尾元素的能力。

2)棧介面卡

  棧介面卡特有的操作:

  棧預設基於deque實現,也可以在list或vector之上實現。

操作 說明
s.pop() 刪除棧頂元素,但不返回該元素值
s.push(item) 建立一個新元素壓入棧頂,該元素通過拷貝或移動item而來,或者由args構造
s.emplace(args)  
s.top() 返回棧頂元素,但不將元素彈出棧

  每個容器介面卡都基於底層容器型別的操作定義了自己的特殊操作。我們只可以使用介面卡操作,而不能使用底層容器型別的操作。

3)佇列介面卡

  queue和priority_queue介面卡定義在queue標頭檔案中。他們所支援的操作:

  queue預設基於deque實現,也可以用list或vector實現。

  priority_queue預設基於vector實現,也可以用deque實現。

操作 說明
q.pop() 刪除queue的首元素或priority_queue的最高優先順序的元素,但不返回此元素
q.front() 返回首元素或尾元素,但不刪除此元素
q.back() (只適用於queue)
q.top() 返回最高優先順序元素,但不刪除該元素(只適用於priority_queue)
q.push(item) 在queue末尾或priority_queue中恰當的位置建立一個元素,其值為item,或者由args構造
q.emplace(args)