适配器(adapter):适配器是标准库中的通用概念,适配器是一种机制,包括容器适配器、迭代器适配器、函数适配器。本质上,适配器是使一事物的行为类似于另一事物的行为的一种机制。
容器适配器让一种已存在的容器类型以一种不同的抽象类型的工作方式实现。
STL提供了三种顺序容器适配器,queue、priority_queue、stack。
默认的stack和queue都基于deque实现,priority_queue基于vector实现。
每一种适配器都定义了两个构造函数,如下:
A a;//创建空适配器a
A a(c);//用一个容器c初始化适配器a
创建适配器时通过将一个顺序容器指定为适配器的第二个类型实参,可覆盖其关联的基础容器类型,例如:
stack< string, vector<string> > str_stk;
stack要求提供push_back,pop_back和back操作
stack可以建立在vector,list或者deque上。
queue要求基础容器提供back,push_back,front,push_front操作,所以可以建立在list和deque上但是不能建立在vector上(不能提供push_front操作)。
priority_queue除了front,push_back和pop_back操作之外还要求随机访问能力,因此可以建立在vector或deque上。但是不能建立在list上
stack提供的操作:
push() 在栈顶增加元素
top() 返回栈顶元素
pop() 移除栈顶元素,只有pop删除元素(无返回值)
empty() 堆栈为空则返回真
size() 返回栈中元素数目
入栈,如例:s.push(x);
访问栈顶,如例:s.top()
出栈,如例:s.pop();注意,出栈操作只是删除栈顶元素,并不返回该元素。
判断栈空,如例:s.empty(),当栈空时,返回true。
访问栈中的元素个数,如例:s.size()。
下面给出stack的操作使用范例:
- #include <iostream>
- #include <vector>
- #include <stack>
- using namespace std;
- int main()
- {
- // 下面程序摘自《c++ primer》中文第四版301页,本例短小精辟,用到了所有操作,所以就不自己写了
- // number of elements we'll put in our stack
- const stack<int>::size_type stk_size = 10;
- stack<int> intStack;
- // 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 err_cnt = 0;
- // look at the top element of the stack
- while(intStack.empty == false){
- int value = intStack.top();
- // read the top element of stack
- if(value != --ix){
- cerr << "oops! expected " << ix << " received " << value << endl;
- ++err_cnt;
- }
- intStack.pop();// pop the top element, and repeat
- }
- cout << "Our program ran with " << err_cnt << " errors" << endl;
- return 0;
- }
STL的顺序容器还有最后两种,这两种都是适配器,分别是queue和priority_queue,这两种容器用法基本相同,所以就放在一块说了。他俩的构造函数形式与stack完全一样,而且都包含在queue头文件中。
他们提供的操作如下:
queue:
push() 在末尾加入一个元素
front() 返回第一个元素
pop() 删除第一个元素
back() 返回最后一个元素
empty() 如果队列空则返回真
size() 返回队列中元素的个数
入队,如例:q.push(x); 将x 接到队列的末端。
访问队首元素,如例:q.front(),即最早被压入队列的元素。
出队,如例:q.pop(); 弹出队列的第一个元素,注意,并不会返回被弹出元素的值。
访问队尾元素,如例:q.back(),即最后被压入队列的元素。
判断队列空,如例:q.empty(),当队列空时,返回true。
访问队列中的元素个数,如例:q.size().
- #include <iostream>
- #include <list>
- #include <queue>
- #include <string>
- using namespace std;
- void show(list<int>& l)
- {
- cout << "list: ";
- for(list<int>::iterator it = l.begin(); it != l.end(); ++it)
- cout << *it << ' ';
- cout << endl;
- }
- void show(queue<int, list<int> > q)
- {
- cout << "queue: ";
- while(!q.empty()){
- cout << q.front() << ' ';
- q.pop();
- }
- cout << endl;
- }
- int main()
- {
- list<int> int_lst;
- int_lst.push_back(1);
- int_lst.push_back(2);
- int_lst.push_back(3);
- int_lst.push_front(0);
- //show list
- show(int_lst);
- queue<int, list<int>> int_que(int_lst);
- //push
- cout << "push 4 into queue" << endl;
- int_que.push(4);
- show(int_que);
- //front and back element
- cout << "front: " << int_que.front() << endl;
- cout << "back: " << int_que.back() << endl;
- //pop
- cout << "pop element" << endl;
- int_que.pop();
- show(int_que);
- //size
- cout << "size of queue: " << int_que.size() << endl;
- return 0;
- }
运行结果如下图:
priority_queue:
在<queue>头文件中,还定义了另一个非常有用的模板类priority_queue(优先队列),
优先队列与队列的差别在于优先队列不是按照入队的顺序出队,而是按照队列中元素的优先权顺序出队(默认为大者优先,也可以通过指定算子来指定自己的优先顺序)。
priority_queue 模板类有三个模板参数,第一个是元素类型,第二个容器类型,第三个是比较算子。其中后两个都可以省略,默认容器为vector,默认算子为less,即小的往前排,大的往后排(出队时序列尾的元素出队)。
定义priority_queue 对象的示例代码如下:
priority_queue<int> q1;
priority_queue< pair<int, int> > q2; // 注意在两个尖括号之间一定要留空格。
priority_queue<int, vector<int>, greater<int> > q3; // 定义小的先出队
priority_queue 的基本操作与queue 相同。
初学者在使用priority_queue 时,最困难的可能就是如何定义比较算子了。
如果是基本数据类型,或已定义了比较运算符的类,可以直接用STL 的less 算子和greater算子——默认为使用less 算子,即小的往前排,大的先出队。
如果要定义自己的比较算子,方法有多种,这里介绍其中的一种:重载比较运算符。优先队列试图将两个元素x 和y 代入比较运算符(对less 算子,调用x<y,对greater 算子,调用x>y),若结果为真,则x 排在y 前面,y 将先于x 出队,反之,则将y 排在x 前面,x 将先出队。
push() 加入一个元素
top() 返回优先队列中有最高优先级的元素
pop() 删除第一个元素
empty() 如果优先队列为空,则返回真
size() 返回优先队列中拥有的元素的个数
从上面可以看出,两种容器的操作非常相似,只是queue特有front()和back(),priority_queue特有top(),仅此而已。
#include <iostream>
#include <queue>
using namespace std;
class T
{
public:
int x, y, z;
T(int a, int b, int c):x(a), y(b), z(c)
{
}
};
bool operator < (const T &t1, const T &t2)
{
return t1.z < t2.z; // 按照z 的顺序来决定t1 和t2 的顺序(出队时序列尾的元素出队)大->小
}
int main()
{
priority_queue<T> q;
q.push(T(4,4,3));
q.push(T(2,2,5));
q.push(T(1,5,4));
q.push(T(3,3,6));
while (!q.empty())
{
T t = q.top(); q.pop();
cout << t.x << " " << t.y << " " << t.z << endl;
}
return 0;
}
输出结果为(注意是按照z 的顺序从大到小出队的):
3 3 6
2 2 5
1 5 4
4 4 3
再看一个按照z 的顺序从小到大出队的例子:
#include <iostream>
#include <queue>
using namespace std;
class T
{
public:
int x, y, z;
T(int a, int b, int c):x(a), y(b), z(c)
{
}
};
bool operator > (const T &t1, const T &t2)
{
return t1.z > t2.z;
}
main()
{
priority_queue<T, vector<T>, greater<T> > q;
q.push(T(4,4,3));
q.push(T(2,2,5));
q.push(T(1,5,4));
q.push(T(3,3,6));
while (!q.empty())
{
T t = q.top(); q.pop();
cout << t.x << " " << t.y << " " << t.z << endl;
}
return 0;
}
输出结果为:
4 4 3
1 5 4
2 2 5
3 3 6
如果我们把第一个例子中的比较运算符重载为:
bool operator < (const T &t1, const T &t2)
{
return t1.z > t2.z; // 按照z 的顺序来决定t1 和t2 的顺序
}
则第一个例子的程序会得到和第二个例子的程序相同的输出结果。