C++標(biāo)準(zhǔn)庫(kù)學(xué)習(xí)(一):順序容器

參考書(shū)籍:C++ primer 第四版

順序容器:它將單一類型元素聚集起來(lái)成為容器,然后根據(jù)位置來(lái)存儲(chǔ)和訪問(wèn)這些元素。

標(biāo)準(zhǔn)庫(kù)定義了三種順序容器:vector、list和deque,他們的差別在于訪問(wèn)元素訪問(wèn)元素的方式,以及添加或刪除元素相關(guān)操作的運(yùn)行代價(jià)。vector支持快速隨機(jī)訪問(wèn),list支持快速插入/刪除,deque是一個(gè)雙端隊(duì)列。
標(biāo)準(zhǔn)庫(kù)來(lái)提供了三種容器適配器。實(shí)際上,適配器是根據(jù)原始的容器類型所提供的操作,通過(guò)定義新的操作接口,來(lái)適應(yīng)基礎(chǔ)的容器類型。順序容器適配器包括stack、queue和priority_queue類型。

容器只定義了少量操作,大多數(shù)額外操作則有算法庫(kù)提供。容器類型的操作集合具有以下層次結(jié)構(gòu)特點(diǎn):一些操作適用于所有容器類型;另外一些操作則只適用于順序或關(guān)聯(lián)容器類型;還有一些操作只適用于順序或關(guān)聯(lián)容器類型的一個(gè)子集。

1 順序容器的定義

所有的容器都是類模版,要定義某種特殊的容器,必須在容器后的尖括號(hào)內(nèi)提供存放元素的數(shù)據(jù)類型。。容器元素類型必須滿足以下兩個(gè)約束:元素類型必須支持賦值運(yùn)算; 元素類型的對(duì)象必須可以復(fù)制。C++ 語(yǔ)言中,大多數(shù)類型都可用作容器的元素類型。

vector<string> svec;

所有容器類型都定義了默認(rèn)構(gòu)造函數(shù),用于創(chuàng)建指定類型的空容器對(duì)象。**為了使程序更清晰、簡(jiǎn)短,容器類型最常用的構(gòu)造函數(shù)是默認(rèn)構(gòu)造函數(shù)。在大多數(shù)的程序中,使用默認(rèn)構(gòu)造函數(shù)能達(dá)到最佳運(yùn)行時(shí)性能,并且使容器更容易使用。 **除了默認(rèn)構(gòu)造函數(shù),容器類型還提供其他的構(gòu)造函數(shù),使程序員可以指定元素初值。

**將一個(gè)容器初始化為另一個(gè)容器的副本 **
將一個(gè)容器復(fù)制給另一個(gè)容器時(shí),類型必須匹配:容器類型和元素類型都必須相同。

vector<string> svec;
vector<string> svec2(svec);

初始化為一段元素的副本
盡管不能直接將一種容器內(nèi)的元素復(fù)制給另一種容器,但系統(tǒng)允許通過(guò)傳遞一對(duì)迭代器間接實(shí)現(xiàn)該實(shí)現(xiàn)該功能。使用迭代器時(shí),不要求容器類型相同。容器內(nèi)元素類型也可以不相同,只要它們相互兼容,能夠?qū)⒁獜?fù)制的元素轉(zhuǎn)換為所構(gòu)建的新容器的元素類型,即可實(shí)現(xiàn)復(fù)制。

 list<string> slist(svec.begin(), svec.end()); 

**分配和初始化指定數(shù)目的元素 **
創(chuàng)建順序容器時(shí),可顯式指定容器大小和一個(gè)(可選的)元素初始化式。容器大小可以是常量或非常量表達(dá)式,元素初始化則必須是可用于初始化其元素類型的對(duì)象的值。

const list<int>::size_type list_size = 64; 
list<string> slist(list_size, "eh");

*注:接受容器大小做形參的構(gòu)造函數(shù)只適用于順序容器,而關(guān)聯(lián)容器不支持這種初始化。 *

2 迭代器

與容器類型一樣,所有迭代器具有相同的接口。例如,所有容器迭代器都支持以解引用運(yùn)算從容器中讀入一個(gè)元素,所有容器都提供自增和自減操作符來(lái)支持從一個(gè)元素到下一個(gè)元素的訪問(wèn)。

關(guān)系操作符只適用于 vector 和 deque 容器,這是因?yàn)橹挥羞@種兩種容器為其元素提供快速、隨機(jī)的訪問(wèn)。它們確??筛鶕?jù)元素位置直接有效地訪問(wèn)指定的容器元素。這兩種容器都支持通過(guò)元素位置實(shí)現(xiàn)的隨機(jī)訪問(wèn),因此它們的迭代器可以有效地實(shí)現(xiàn)算術(shù)和關(guān)系運(yùn)算。

//用于計(jì)算vector對(duì)象的重點(diǎn)位置
vector<int>::iterator iter = vec.begin() + vec.size()/2; 

:所有的容器都支持iter1 ==iter2iter1!=iter2,而只有vector 和 deque 才支持iter1 <=iter2等關(guān)系運(yùn)算。所以在用迭代器遍歷容器時(shí),最好使用iter1!=iter2來(lái)判斷終止。

一些容器操作會(huì)修改容器的內(nèi)在狀態(tài)或移動(dòng)容器內(nèi)的元素,這樣會(huì)使所有指向被移動(dòng)的元素的迭代器失效,也可能同時(shí)使其他迭代器失效。使用無(wú)效迭代器是沒(méi)有定義的,可能會(huì)導(dǎo)致與懸垂指針相同的問(wèn)題。 例如,每種容器都定義了一個(gè)或多個(gè) erase 函數(shù)。這些函數(shù)提供了刪除容器元素的功能。任何指向已刪除元素的迭代器都具有無(wú)效值,畢竟,該迭代器指向了容器中不再存在的元素。

3 順序容器的常用操作

容器定義的操作非常少,只定義了構(gòu)造函數(shù)、添加或刪除元素的操作、設(shè)置容器長(zhǎng)度的操作以及返回指向特殊元素的迭代器的操作。其他一些有用的操作,如排序、查找,則不是由容器類型定義,而是由標(biāo)準(zhǔn)算法定義。

3.1 begin 和 end 成員

begin 和 end 操作產(chǎn)生指向容器內(nèi)第一個(gè)元素和最后一個(gè)元素的下一位置的迭代器,這兩個(gè)迭代器通常用于標(biāo)記包含容器中所有元素的迭代器范圍。

迭代器操作

3.2 添加元素

添加元素的方法.png
vector<string> container;
string text_word; 
while (cin >> text_word) 
     container.push_back(text_word); 

list<int> ilist; 
for (size_t ix = 0; ix != 4; ++ix) 
     ilist.push_front(ix); 

任何 insert 或 push 操作都可能導(dǎo)致迭代器失效。當(dāng)編寫(xiě)循環(huán)將元素插入到 vector 或 deque 容器中時(shí),程序必須確保迭代器在每次循環(huán)后都得到更新。為了避免存儲(chǔ) end 迭代器,可以在每次做完插入運(yùn)算后重新計(jì)算 end 迭代器值:

vector<int>::iterator first = v.begin();//不要令last = v.end();
while (first != v.end()) 
{ 
     first = v.insert(first, 42); // insert new value 
     ++first; // advance first just past the element we added 
} 

3.3 順序容器大小的操作

順序容器大小的操作.png

3.4 訪問(wèn)元素

訪問(wèn)元素的操作.png
if (!ilist.empty()) { 
     list<int>::reference val = *ilist.begin(); 
     list<int>::reference val2 = ilist.front(); 

     list<int>::reference last = *--ilist.end(); 
     list<int>::reference last2 = ilist.back(); 
} 

在調(diào)用 front 或 back 函數(shù)之前,或者在對(duì) begin 或 end 返回的迭代器進(jìn)行解引用運(yùn)算之前,必須保證 ilist 容器非空。如果該 list 容器為空,則 if 語(yǔ)句內(nèi)所有的操作都沒(méi)有定義。

在使用下標(biāo)運(yùn)算時(shí),必須保證在指定下標(biāo)位置上的元素確實(shí)存在,因?yàn)橄聵?biāo)操作符本身不會(huì)做相關(guān)的檢查。使用下標(biāo)運(yùn)算的另一個(gè)可選方案是 at 成員函數(shù),這個(gè)函數(shù)的行為和下標(biāo)運(yùn)算相似,但是如果給出的下標(biāo)無(wú)效,at 函數(shù)將會(huì)拋出 out_of_range 異常。

3.5 刪除元素

刪除順序容器內(nèi)元素的操作.png

pop_front 和 pop_back 函數(shù)的返回值并不是刪除的元素值,而是 void。要獲取刪除的元素值,則必須在刪除元素之前調(diào)用 front 或 back 函數(shù)。

while (!ilist.empty()) { 
         process(ilist.front()); // do something with the current top of ilist 
         ilist.pop_front();      // done; remove first element 
     } 

erase 操作不會(huì)檢查它的參數(shù),因此必須確保用作參數(shù)的迭代器或迭代器范圍是有效的。通常,程序員必須在容器中找出要?jiǎng)h除的元素后,才使用 erase 操作。尋找一個(gè)指定元素的最簡(jiǎn)單方法是使用標(biāo)準(zhǔn)庫(kù)的 find 算法。

string searchValue("Quasimodo"); 
list<string>::iterator iter = find(slist.begin(), slist.end(), searchValue); 
if (iter != slist.end())    slist.erase(iter); 

3.6 swap交換操作

swap 操作實(shí)現(xiàn)交換兩個(gè)容器內(nèi)所有元素的功能。要交換的操作數(shù)必須是相同類型的容器,而且所存儲(chǔ)的元素類型也必須相同。調(diào)用了 swap 函數(shù)后,右操作數(shù)原來(lái)存儲(chǔ)的元素被存放在左操作數(shù)中,反之亦然。 **該操作不會(huì)刪除或插入任何元素,而且保證在常量時(shí)間內(nèi)實(shí)現(xiàn)交換。由于容器內(nèi)沒(méi)有移動(dòng)任何元素,因此迭代器不會(huì)失效。 **

4 容器的選用

程序應(yīng)根據(jù)訪問(wèn)、添加、刪除容器元素所需的代價(jià)決定選擇哪種類型的容器。vector 和 deque 容器提供了對(duì)元素的快速隨機(jī)訪問(wèn),但付出的代價(jià)是,在容器的任意位置插入或刪除元素,比在容器尾部插入和刪除的開(kāi)銷(xiāo)更大。list 類型在任何位置都能快速插入和刪除,但付出的代價(jià)是元素的隨機(jī)訪問(wèn)開(kāi)銷(xiāo)較大。通常來(lái)說(shuō),除非找到選擇使用其他容器的更好理由,否則 vector 容器都是最佳選擇。

  • 如果程序要求隨機(jī)訪問(wèn)元素,則應(yīng)使用 vector 或 deque 容器。
  • 如果程序必須在容器的中間位置插入或刪除元素,則應(yīng)采用 list 容器。
  • 如果程序不是在容器的中間位置,而是在容器首部或尾部插入或刪除元素,則應(yīng)采用 deque 容器。
  • 如果只需在讀取輸入時(shí)在容器的中間位置插入元素,然后需要隨機(jī)訪問(wèn)元素,則可考慮在輸入時(shí)將元素讀入到一個(gè) list 容器,接著對(duì)此容器重新排序,使其適合順序訪問(wèn),然后將排序后的 list 容器復(fù)制到一個(gè) vector 容器。

如果無(wú)法確定某種應(yīng)用應(yīng)該采用哪種容器,則編寫(xiě)代碼時(shí)嘗試只使用 vector 和 lists 容器都提供的操作:使用迭代器,而不是下標(biāo),并且避免隨機(jī)訪問(wèn)元素。這樣編寫(xiě),在必要時(shí),可很方便地將程序從使用 vector 容器修改為使用 list 的容器。

5 容器適配器

容器適配器讓一種已存在的容器類型采用另一種不同的抽象類型的工作方式實(shí)現(xiàn)。例如,stack(棧)適配器可使任何一種順序容器以棧的方式工作。

所有適配器都定義了兩個(gè)構(gòu)造函數(shù):默認(rèn)構(gòu)造函數(shù)用于創(chuàng)建空對(duì)象,而帶一個(gè)容器參數(shù)的構(gòu)造函數(shù)將參數(shù)容器的副本作為其基礎(chǔ)值。默認(rèn)的 stack 和 queue 都基于 deque 容器實(shí)現(xiàn),而 priority_queue 則在 vector 容器上實(shí)現(xiàn)。在創(chuàng)建適配器時(shí),通過(guò)將一個(gè)順序容器指定為適配器的第二個(gè)類型實(shí)參,可覆蓋其關(guān)聯(lián)的基礎(chǔ)容器類型。

 stack<int> stk(deq);
 stack< string, vector<string> > str_stk; 
 stack<string, vector<string> > str_stk2(svec); 

對(duì)于給定的適配器,其關(guān)聯(lián)的容器必須滿足一定的約束條件。stack 適配器所關(guān)聯(lián)的基礎(chǔ)容器可以是任意一種順序容器類型。因此,stack ??梢越⒃?vector、list 或者 deque 容器之上。而 queue 適配器要求其關(guān)聯(lián)的基礎(chǔ)容器必須提供 push_front 運(yùn)算,因此只能建立在 list 容器上,而不能建立在 vector 容器上。priority_queue 適配器要求提供隨機(jī)訪問(wèn)功能,因此可建立在 vector 或 deque 容器上,但不能建立在 list 容器上。

棧容器適配器支持的操作

priority_queue 允許用戶為隊(duì)列中存儲(chǔ)的元素設(shè)置優(yōu)先級(jí)。這種隊(duì)列不是直接將新元素放置在隊(duì)列尾部,而是放在比它優(yōu)先級(jí)低的元素前面。標(biāo)準(zhǔn)庫(kù)默認(rèn)使用元素類型的 < 操作符來(lái)確定它們之間的優(yōu)先級(jí)關(guān)系。


隊(duì)列和優(yōu)先級(jí)隊(duì)列支持的操作
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容