《C++Primer》第九章-順序容器-學習筆記(3)-容器適配器&棧&隊列

《C++Primer》第九章-順序容器-學習筆記(3)-容器適配器&棧&隊列

日誌:
1,2020-03-12 筆者提交文章的初版V1.0

作者按:
最近在學習C++ primer,初步打算把所學的記錄下來。

傳送門/推廣
《C++Primer》第二章-變量和基本類型-學習筆記(1)
《C++Primer》第三章-標準庫類型-學習筆記(1)
《C++Primer》第八章-標準 IO 庫-學習筆記(1)
《C++Primer》第十二章-類-學習筆記(1)

容器適配器

除了順序容器,標準庫還提供了三種順序容器適配器隊列(queue)優先級隊列(priority_queue)棧(stack)
適配器(adaptor)是標準庫中通用的概念,包括容器適配器迭代器適配器函數適配器
適配器本質上是使一事物的行爲類似於另一事物的行爲的一種機制。 容器適配器讓一種已存在的容器類型採用另一種不同的抽象類型的工作方式實現。例如,stack(棧)適配器可使任何一種順序容器以棧的方式工作。表 9.22 列出了所有容器適配器通用的操作和類型。

表 1. 適配器通用的操作和類型:

操作和類型 作用
size_type 一種類型,足以存儲此適配器類型最大對象的長度
value_type 元素類型
container_type 基礎容器的類型,適配器在此容器類型上實現
A a; 創建一個新空適配器,命名爲 a
A a( c ); 創建一個名爲 a 的新適配器,初始化爲容器 c 的副本
關係操作符 所有適配器都支持全部關係操作符:==、 !=、 <、 <=、 >、>=

使用適配器時,必須包含相關的頭文件:

#include <stack> // stack adaptor
#include <queue> // both queue and priority_queue adaptors

適配器的初始化

所有適配器都定義了兩個構造函數默認構造函數用於創建空對象,而帶一個容器參數的構造函數將參數容器的副本作爲其基礎值。例如,假設 deq deque 類型的容器,則可用 deq 初始化一個新的棧,如下所示:

stack<int> stk(deq); // copies elements from deq into stk
//帶一個容器參數的構造函數`將參數容器的副本作爲其基礎值

覆蓋基礎容器類型

默認的stack 和 queue基於 deque 容器實現priority_queue基於 vector 容器實現。在創建適配器時,通過將一個順序容器指定爲適配器的第二個類型實參,可覆蓋其關聯的基礎容器類型:

// empty stack implemented on top of vector
stack< string, vector<string> > str_stk;  //將一個順序容器指定爲適配器的第二個類型實參,可覆蓋stack關聯的基礎容器類型deque
// str_stk2 is implemented on top of vector and holds a copy of svec
stack<string, vector<string> > str_stk2(svec);

對於給定的適配器,其關聯的容器必須滿足一定的約束條件。

  • stack 適配器所關聯的基礎容器可以是任意一種順序容器類型。因此,stack 棧可以建立在vector、list 或者 deque 容器之上。
  • queue 適配器要求其關聯的基礎容器必須提供 push_front 運算,因此只能建立在 list 或deque容器上,而不能建立在vector 容器上。
  • priority_queue 適配器要求提供隨機訪問功能,因此可建立在vector 或 deque 容器上,但不能建立在 list 容器上。

適配器的關係運算

兩個相同類型的適配器可以做相等、不等、小於、大於、小於等於以及等於關係比較,只要基礎元素類型支持等於和小於操作符既可。這些關係運算由元素依次比較來實現。第一對不相等的元素將決定兩者之間的小於或大於關係。

棧適配器

表 2列出了棧提供的所有操作。
表 2. 棧容器適配器支持的操作:

棧容器適配器操作 作用
s.empty() 如果棧爲空,則返回 true,否則返回 stack
s.size() 返回棧中元素的個數
s.pop() 刪除棧頂元素的值,但不返回其值
s.top() 返回棧頂元素的值,但不刪除該元素
s.push(item) 在棧頂壓入新元素

棧容器適配器支持的操作程序實例:

// number of elements we'll put in our stack
const stack<int>::size_type stk_size = 10;  //打算放入10個元素到棧中
stack<int> intStack; // empty stack  初始化一個棧
// fill up the stack
int ix = 0;
while (intStack.size() != stk_size) 
// use postfix increment; want to push old value onto intStack
	intStack.push(ix++); // intStack holds 0...9 inclusive  
int error_cnt = 0;  //用來檢查元素錯誤的數量
// look at each value and pop it off the stack
while (intStack.empty() == false)
 {
	int value = intStack.top();
// read the top element of the stack
	if (value != --ix) 
	{
		cerr << "oops! expected " << ix<< " received " << value << endl;
		++error_cnt; 
	}
	intStack.pop(); // pop the top element, and repeat
}
cout << "Our program ran with "<< error_cnt << " errors!" << endl;

聲明語句:

stack<int> intStack; // empty stack

將 intStack 定義爲一個存儲整型元素的空棧。第一個 while 循環在該棧中添加了 stk_size 個元素,元素初值是從 0 開始依次遞增 1 的整數。第二個while 循環迭代遍歷整個棧,檢查其棧頂(top)的元素值,然後棧頂元素出棧,直到棧變空爲止。
所有容器適配器都根據其基礎容器類型所支持的操作來定義自己的操作。默認情況下,棧適配器建立在 deque 容器上,因此採用 deque 提供的操作來實現棧功能。例如,執行下面的語句:

// use postfix increment; want to push old value onto intStack
intStack.push(ix++); // intStack holds 0...9 inclusive

這個操作通過調用 push_back 操作實現,而該 intStack 所基於的 deque對象提供。儘管棧是以 deque 容器爲基礎實現的,但是程序員不能直接訪問deque 所提供的操作。例如,不能在棧上調用push_back 函數,而是必須使用棧所提供的名爲 push 的操作。

隊列和優先級隊列

標準庫隊列使用了先進先出(FIFO)的存儲和檢索策略。進入隊列的對象被放置在尾部,下一個被取出的元素則取自隊列的首部。標準庫提供了兩種風格的隊列:FIFO 隊列(FIFO queue,簡稱 queue),以及優先級隊列(priority queue)
priority_queue允許用戶爲隊列中存儲的元素設置優先級。這種隊列不是直接將新元素放置在隊列尾部,而是放在比它優先級低的元素前面。標準庫默認使用元素類型的 <操作符來確定它們之間的優先級關係。
優先級隊列的一個實例機場行李檢查隊列。30 分鐘後即將離港的航班的乘客通常會被移到隊列前面,以便他們能在飛機起飛前完成檢查過程。使用優先級隊列的程序示例是操作系統的調試表,它決定在大量等待進程中下一個要執行的進程。
要使用FIFO 隊列與優先級隊列,必須包含 queue 頭文件。表 3列出了隊列和優先級隊列所提供的所有操作。
表 3. 隊列和優先級隊列支持的操作:

隊列和優先級隊列操作 作用
q.empty() 如果隊列爲空,則返回 true,否則返回 false
q.size() 返回隊列中元素的個數
q.pop() 刪除隊首元素,但不返回其值
q.front() 返回隊首元素的值,但不刪除該元素。(該操作只適用於隊列)
q.back() 返回隊尾元素的值,但不刪除該元素。(該操作只適用於隊列)
q.top() 返回具有最高優先級的元素值,但不刪除該元素。(該操作只適用於優先級隊列)
q.push(item) 對於 queue,在隊尾壓入一個新元素,對於 priority_quue,在基於優先級的適當位置插入新元素

小結

C++ 標準庫定義了一系列順序容器類型。容器是用於存儲某種給定類型對象的模板類型。在順序容器中,所有元素根據其位置排列和訪問。順序容器共享一組通用的已標準化的接口:如果兩種順序容器都提供某一操作,那麼該操作具有相同的接口和含義。

所有容器都提供(有效的)動態內存管理。程序員在容器中添加元素時,不必操心元素存放在哪裏。容器自己實現其存儲管理。

最經常使用的容器類型是vector,它支持對元素的快速隨機訪問。可高效地在 vector 容器尾部添加和刪除元素,而在其他任何位置上的插入或刪除運算則要付出比較昂貴的代價。
deque 類與 vector 相似,但它還支持在 deque 首部的快速插入和刪除運算
list 類只支持元素的順序訪問,但在 list 內部任何位置插入和刪除元素都非常快速。

容器定義的操作非常少,只定義了構造函數、添加或刪除元素的操作、設置容器長度的操作以及返回指向特殊元素的迭代器的操作。其他一些有用的操作,如排序、查找,則不是由容器類型定義,而是由第十一章介紹的標準算法定義。
在容器中添加或刪除元素可能會使已存在的迭代器失效。當混合使用迭代器操作和容器操作時,必須時刻留意給定的容器操作是否會使迭代器失效。許多使一個迭代器失效的操作,例如 insert 或 erase,將返回一個新的迭代器,讓程序員保留容器中的一個位置。使用改變容器長度的容器操作的循環必須非常小心其迭代器的使用。

參考資料

【1】C++ Primer 中文版(第四版·特別版)

註解

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章