除了顺序容器,标准库还提供了三种顺序容器适配器:queue、priority_queue 和 stack。
适配器(adaptor)是标准库中通用的概念,包括容器适配器、迭代器适配器和函数适配器。
本质上,适配器是使一事物的行为类似于另一事物的行为的一种机制。
容器适配器让一种已存在的容器类型采用另一种不同的抽象类型的工作方式实现。
例如,stack(栈)适配器可使任何一种顺序容器以栈的方式工作。
表 9.22 列出了所有容器适配器通用的操作和类型。
// 表 9.22. 适配器通用的操作和类型 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<int> 类型的容器,则可用 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; // 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 容器上,而不能建立在 vector 容器上。
priority_queue 适配器要求提供随机访问功能,
因此可建立在 vector 或 deque 容器上,但不能建立在 list 容器上。
适配器的关系运算
两个相同类型的适配器可以做相等、不等、小于、大于、小于等于以及等于关系比较,
只要基础元素类型支持等于和小于操作符既可。
这些关系运算由元素依次比较来实现。第一对不相等的元素将决定两者之间的小于或大于关系。
[1. 栈适配器]
// 表 9.23. 栈容器适配器支持的操作 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; 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 的操作。
[2. 队列和优先级队列]
标准库队列使用了先进先出(FIFO)的存储和检索策略。
进入队列的对象被放置在尾部,下一个被取出的元素则取自队列的首部。
标准库提供了两种风格的队列:FIFO 队列(FIFO queue,简称 queue),以及优先级队列(priority queue)。
priority_queue 允许用户为队列中存储的元素设置优先级。
这种队列不是直接将新元素放置在队列尾部,而是放在比它优先级低的元素前面。
标准库默认使用元素类型的 "<" 操作符来确定它们之间的优先级关系。
优先级队列的一个实例应用 —— 机场行李检查队列。
30 分钟后即将离港的航班的乘客通常会被移到队列前面,以便他们能在飞机起飞前完成检查过程。
使用优先级队列的程序示例是操作系统的调试表,它决定在大量等待进程中下一个要执行的进程。
要使用这两种队列,必须包含 queue 头文件。
表 9.24 列出了队列和优先级队列所提供的所有操作。
// 表 9.24. 队列和优先级队列支持的操作 q.empty() 如果队列为空,则返回 true,否则返回 false q.size() 返回队列中元素的个数 q.pop() 删除队首元素,但不返回其值 q.front() 返回队首元素的值,但不删除该元素 该操作只适用于队列 q.back() 返回队尾元素的值,但不删除该元素 该操作只适用于队列 q.top() 返回具有最高优先级的元素值,但不删除该元素 该操作只适用于优先级队列 q.push(item) 对于 queue,在队尾压入一个新元素, 对于 priority_quue,在基于优先级的适当位置插入新元素