參考書籍:C++ primer 第四版
順序容器:它將單一類型元素聚集起來成為容器苫耸,然后根據(jù)位置來存儲和訪問這些元素。
標準庫定義了三種順序容器:vector涧狮、list和deque,他們的差別在于訪問元素訪問元素的方式,以及添加或刪除元素相關操作的運行代價管呵。vector支持快速隨機訪問,list支持快速插入/刪除哺窄,deque是一個雙端隊列撇寞。
標準庫來提供了三種容器適配器顿天。實際上,適配器是根據(jù)原始的容器類型所提供的操作蔑担,通過定義新的操作接口牌废,來適應基礎的容器類型。順序容器適配器包括stack啤握、queue和priority_queue類型鸟缕。
容器只定義了少量操作,大多數(shù)額外操作則有算法庫提供排抬。容器類型的操作集合具有以下層次結構特點:一些操作適用于所有容器類型懂从;另外一些操作則只適用于順序或關聯(lián)容器類型;還有一些操作只適用于順序或關聯(lián)容器類型的一個子集蹲蒲。
1 順序容器的定義
所有的容器都是類模版番甩,要定義某種特殊的容器,必須在容器后的尖括號內提供存放元素的數(shù)據(jù)類型届搁。缘薛。容器元素類型必須滿足以下兩個約束:元素類型必須支持賦值運算; 元素類型的對象必須可以復制卡睦。C++ 語言中宴胧,大多數(shù)類型都可用作容器的元素類型。
vector<string> svec;
所有容器類型都定義了默認構造函數(shù)表锻,用于創(chuàng)建指定類型的空容器對象恕齐。**為了使程序更清晰、簡短瞬逊,容器類型最常用的構造函數(shù)是默認構造函數(shù)显歧。在大多數(shù)的程序中,使用默認構造函數(shù)能達到最佳運行時性能确镊,并且使容器更容易使用士骤。 **除了默認構造函數(shù),容器類型還提供其他的構造函數(shù)骚腥,使程序員可以指定元素初值。
**將一個容器初始化為另一個容器的副本 **
將一個容器復制給另一個容器時瓶逃,類型必須匹配:容器類型和元素類型都必須相同束铭。
vector<string> svec;
vector<string> svec2(svec);
初始化為一段元素的副本
盡管不能直接將一種容器內的元素復制給另一種容器,但系統(tǒng)允許通過傳遞一對迭代器間接實現(xiàn)該實現(xiàn)該功能厢绝。使用迭代器時契沫,不要求容器類型相同。容器內元素類型也可以不相同昔汉,只要它們相互兼容懈万,能夠將要復制的元素轉換為所構建的新容器的元素類型,即可實現(xiàn)復制。
list<string> slist(svec.begin(), svec.end());
**分配和初始化指定數(shù)目的元素 **
創(chuàng)建順序容器時会通,可顯式指定容器大小和一個(可選的)元素初始化式口予。容器大小可以是常量或非常量表達式,元素初始化則必須是可用于初始化其元素類型的對象的值涕侈。
const list<int>::size_type list_size = 64;
list<string> slist(list_size, "eh");
*注:接受容器大小做形參的構造函數(shù)只適用于順序容器沪停,而關聯(lián)容器不支持這種初始化。 *
2 迭代器
與容器類型一樣裳涛,所有迭代器具有相同的接口木张。例如,所有容器迭代器都支持以解引用運算從容器中讀入一個元素端三,所有容器都提供自增和自減操作符來支持從一個元素到下一個元素的訪問舷礼。
關系操作符只適用于 vector 和 deque 容器,這是因為只有這種兩種容器為其元素提供快速郊闯、隨機的訪問妻献。它們確保可根據(jù)元素位置直接有效地訪問指定的容器元素虚婿。這兩種容器都支持通過元素位置實現(xiàn)的隨機訪問旋奢,因此它們的迭代器可以有效地實現(xiàn)算術和關系運算。
//用于計算vector對象的重點位置
vector<int>::iterator iter = vec.begin() + vec.size()/2;
注:所有的容器都支持iter1 ==iter2
和iter1!=iter2
然痊,而只有vector 和 deque 才支持iter1 <=iter2
等關系運算至朗。所以在用迭代器遍歷容器時,最好使用iter1!=iter2
來判斷終止剧浸。
一些容器操作會修改容器的內在狀態(tài)或移動容器內的元素锹引,這樣會使所有指向被移動的元素的迭代器失效,也可能同時使其他迭代器失效唆香。使用無效迭代器是沒有定義的嫌变,可能會導致與懸垂指針相同的問題。 例如躬它,每種容器都定義了一個或多個 erase 函數(shù)腾啥。這些函數(shù)提供了刪除容器元素的功能。任何指向已刪除元素的迭代器都具有無效值冯吓,畢竟倘待,該迭代器指向了容器中不再存在的元素。
3 順序容器的常用操作
容器定義的操作非常少组贺,只定義了構造函數(shù)凸舵、添加或刪除元素的操作、設置容器長度的操作以及返回指向特殊元素的迭代器的操作失尖。其他一些有用的操作啊奄,如排序渐苏、查找,則不是由容器類型定義菇夸,而是由標準算法定義琼富。
3.1 begin 和 end 成員
begin 和 end 操作產(chǎn)生指向容器內第一個元素和最后一個元素的下一位置的迭代器,這兩個迭代器通常用于標記包含容器中所有元素的迭代器范圍峻仇。
3.2 添加元素
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 操作都可能導致迭代器失效公黑。當編寫循環(huán)將元素插入到 vector 或 deque 容器中時,程序必須確保迭代器在每次循環(huán)后都得到更新摄咆。為了避免存儲 end 迭代器凡蚜,可以在每次做完插入運算后重新計算 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 順序容器大小的操作
3.4 訪問元素
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();
}
在調用 front 或 back 函數(shù)之前,或者在對 begin 或 end 返回的迭代器進行解引用運算之前吭从,必須保證 ilist 容器非空朝蜘。如果該 list 容器為空,則 if 語句內所有的操作都沒有定義涩金。
在使用下標運算時谱醇,必須保證在指定下標位置上的元素確實存在,因為下標操作符本身不會做相關的檢查步做。使用下標運算的另一個可選方案是 at 成員函數(shù)副渴,這個函數(shù)的行為和下標運算相似,但是如果給出的下標無效全度,at 函數(shù)將會拋出 out_of_range 異常煮剧。
3.5 刪除元素
pop_front 和 pop_back 函數(shù)的返回值并不是刪除的元素值,而是 void将鸵。要獲取刪除的元素值勉盅,則必須在刪除元素之前調用 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 操作不會檢查它的參數(shù)顶掉,因此必須確保用作參數(shù)的迭代器或迭代器范圍是有效的草娜。通常,程序員必須在容器中找出要刪除的元素后痒筒,才使用 erase 操作宰闰。尋找一個指定元素的最簡單方法是使用標準庫的 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 操作實現(xiàn)交換兩個容器內所有元素的功能簿透。要交換的操作數(shù)必須是相同類型的容器移袍,而且所存儲的元素類型也必須相同。調用了 swap 函數(shù)后萎战,右操作數(shù)原來存儲的元素被存放在左操作數(shù)中咐容,反之亦然舆逃。 **該操作不會刪除或插入任何元素蚂维,而且保證在常量時間內實現(xiàn)交換戳粒。由于容器內沒有移動任何元素,因此迭代器不會失效虫啥。 **
4 容器的選用
程序應根據(jù)訪問蔚约、添加、刪除容器元素所需的代價決定選擇哪種類型的容器涂籽。vector 和 deque 容器提供了對元素的快速隨機訪問苹祟,但付出的代價是,在容器的任意位置插入或刪除元素评雌,比在容器尾部插入和刪除的開銷更大树枫。list 類型在任何位置都能快速插入和刪除,但付出的代價是元素的隨機訪問開銷較大景东。通常來說砂轻,除非找到選擇使用其他容器的更好理由,否則 vector 容器都是最佳選擇斤吐。
- 如果程序要求隨機訪問元素搔涝,則應使用 vector 或 deque 容器。
- 如果程序必須在容器的中間位置插入或刪除元素和措,則應采用 list 容器庄呈。
- 如果程序不是在容器的中間位置,而是在容器首部或尾部插入或刪除元素派阱,則應采用 deque 容器诬留。
- 如果只需在讀取輸入時在容器的中間位置插入元素,然后需要隨機訪問元素颁褂,則可考慮在輸入時將元素讀入到一個 list 容器故响,接著對此容器重新排序,使其適合順序訪問颁独,然后將排序后的 list 容器復制到一個 vector 容器彩届。
如果無法確定某種應用應該采用哪種容器,則編寫代碼時嘗試只使用 vector 和 lists 容器都提供的操作:使用迭代器誓酒,而不是下標樟蠕,并且避免隨機訪問元素。這樣編寫靠柑,在必要時寨辩,可很方便地將程序從使用 vector 容器修改為使用 list 的容器。
5 容器適配器
容器適配器讓一種已存在的容器類型采用另一種不同的抽象類型的工作方式實現(xiàn)歼冰。例如靡狞,stack(棧)適配器可使任何一種順序容器以棧的方式工作。
所有適配器都定義了兩個構造函數(shù):默認構造函數(shù)用于創(chuàng)建空對象隔嫡,而帶一個容器參數(shù)的構造函數(shù)將參數(shù)容器的副本作為其基礎值甸怕。默認的 stack 和 queue 都基于 deque 容器實現(xiàn)甘穿,而 priority_queue 則在 vector 容器上實現(xiàn)。在創(chuàng)建適配器時梢杭,通過將一個順序容器指定為適配器的第二個類型實參温兼,可覆蓋其關聯(lián)的基礎容器類型。
stack<int> stk(deq);
stack< string, vector<string> > str_stk;
stack<string, vector<string> > str_stk2(svec);
對于給定的適配器武契,其關聯(lián)的容器必須滿足一定的約束條件募判。stack 適配器所關聯(lián)的基礎容器可以是任意一種順序容器類型。因此咒唆,stack 椊斓妫可以建立在 vector、list 或者 deque 容器之上全释。而 queue 適配器要求其關聯(lián)的基礎容器必須提供 push_front 運算敦腔,因此只能建立在 list 容器上,而不能建立在 vector 容器上恨溜。priority_queue 適配器要求提供隨機訪問功能符衔,因此可建立在 vector 或 deque 容器上,但不能建立在 list 容器上糟袁。
priority_queue 允許用戶為隊列中存儲的元素設置優(yōu)先級判族。這種隊列不是直接將新元素放置在隊列尾部,而是放在比它優(yōu)先級低的元素前面项戴。標準庫默認使用元素類型的 < 操作符來確定它們之間的優(yōu)先級關系形帮。