那么C++中的容器适配器是干什么的呢?可以做一个类比,我们已有的容器(比如vector、list、deque)就是设备,这个设备支持的操作很多,比如插入,删除,迭代器访问等等。而我们希望这个容器表现出来的是栈的样子:先进后出,入栈出栈等等,此时,我们没有必要重新动手写一个新的数据结构,而是把原来的容器重新封装一下,改变它的接口,就能把它当做栈使用了。
言归正传,理解了什么是适配器以后,其实问题就很简单的。C++中定义了3种容器适配器,它们让容器提供的接口变成了我们常用的的3种数据结构:栈(先进后出)队列(先进先出)和优先级队列(按照优先级(“<”号)排序,而不是按照到来的顺序排序)。
至于具体是怎么变的,我们可以先了解一个大概:默认情况下,栈和队列都是基于deque实现的,而优先级队列则是基于vector实现的。当然,我们也可以指定自己的实现方式。但是由于数据结构的关系,我们也不能胡乱指定。栈的特点是后进先出,所以它关联的基本容器可以是任意一种顺序容器,因为这些容器类型结构都可以提供栈的操作有求,它们都提供了push_back、pop_back和back操作。 队列queue的特点是先进先出,适配器要求其关联的基础容器必须提供pop_front操作,因此其不能建立在vector容器上;对于优先级队列,由于它要求支持随机访问的功能,所以可以建立在vector或者deque上,不能建立在list上。
言归正传,理解了什么是适配器以后,其实问题就很简单的。C++中定义了3种容器适配器,它们让容器提供的接口变成了我们常用的的3种数据结构:栈(先进后出)队列(先进先出)和优先级队列(按照优先级(“<”号)排序,而不是按照到来的顺序排序)。
至于具体是怎么变的,我们可以先了解一个大概:默认情况下,栈和队列都是基于deque实现的,而优先级队列则是基于vector实现的。当然,我们也可以指定自己的实现方式。但是由于数据结构的关系,我们也不能胡乱指定。栈的特点是后进先出,所以它关联的基本容器可以是任意一种顺序容器,因为这些容器类型结构都可以提供栈的操作有求,它们都提供了push_back、pop_back和back操作。 队列queue的特点是先进先出,适配器要求其关联的基础容器必须提供pop_front操作,因此其不能建立在vector容器上;对于优先级队列,由于它要求支持随机访问的功能,所以可以建立在vector或者deque上,不能建立在list上。
让我们看看这三种关联容器提供的接口:
栈支持的操作有:
1.empty() 堆栈为空则返回真
2.pop() 移除栈顶元素
3.push() 在栈顶增加元素
4.size() 返回栈中元素数目
5.top() 返回栈顶元素
队列支持的操作有:
1.back() 返回一个引用,指向最后一个元素
2.empty() 如果队列空则返回真
3.front() 返回第一个元素
4.pop() 删除第一个元素
5.push() 在末尾加入一个元素
6.size() 返回队列中元素的个数
优先级队列支持的操作有:
1.empty() 如果优先队列为空,则返回真
2.pop() 删除第一个元素
3.push() 加入一个元素
4.size() 返回优先队列中拥有的元素的个数
5.top() 返回优先队列中有最高优先级的元素
举个例子:
- int main()
- {
- const stack<int>::size_type stk_size = 10;
- //创建一个空栈
- stack<int> intStack;
- //改变栈的实现方式为vector
- //stack<int,vector<int> > intStack;
- int ix = 0;
- while(intStack.size() != stk_size)
- intStack.push(ix++);
- int error_cnt = 0;
- while(intStack.empty() == false)
- {
- //top操作返回栈顶元素,但并不删除
- int value = intStack.top();
- if(value != --ix)
- {
- cout<<"opps! expected "<<ix<<" received "<<value<<endl;
- ++error_cnt;
- }
- //删除栈顶元素
- intStack.pop();
- }
- cout<<"our program ran with "<<error_cnt<<" errors! "<<endl;
- return 0;
- }
int main()
{
const stack<int>::size_type stk_size = 10;
//创建一个空栈
stack<int> intStack;
//改变栈的实现方式为vector
//stack<int,vector<int> > intStack;
int ix = 0;
while(intStack.size() != stk_size)
intStack.push(ix++);
int error_cnt = 0;
while(intStack.empty() == false)
{
//top操作返回栈顶元素,但并不删除
int value = intStack.top();
if(value != --ix)
{
cout<<"opps! expected "<<ix<<" received "<<value<<endl;
++error_cnt;
}
//删除栈顶元素
intStack.pop();
}
cout<<"our program ran with "<<error_cnt<<" errors! "<<endl;
return 0;
}
最后我们可以窥探一下stl中的源码:
- template<class _Ty,
- class _Container = deque<_Ty> >
- class stack
- { // LIFO queue implemented with a container
- public:
- typedef stack<_Ty, _Container> _Myt;
- typedef _Container container_type;
- typedef typename _Container::value_type value_type;
- typedef typename _Container::size_type size_type;
- typedef typename _Container::reference reference;
- typedef typename _Container::const_reference const_reference;
- stack()
- : c()
- { // construct with empty container
- }
- stack(const _Myt& _Right)
- : c(_Right.c)
- { // construct by copying _Right
- }
- explicit stack(const _Container& _Cont)
- : c(_Cont)
- { // construct by copying specified container
- }
- _Myt& operator=(const _Myt& _Right)
- { // assign by copying _Right
- c = _Right.c;
- return (*this);
- }
- stack(_Myt&& _Right)
- : c(_STD move(_Right.c))
- { // construct by moving _Right
- }
- explicit stack(_Container&& _Cont)
- : c(_STD move(_Cont))
- { // construct by copying specified container
- }
- _Myt& operator=(_Myt&& _Right)
- { // assign by moving _Right
- c = _STD move(_Right.c);
- return (*this);
- }
- void push(value_type&& _Val)
- { // insert element at beginning
- c.push_back(_STD move(_Val));
- }
- template<class _Valty>
- void emplace(_Valty&& _Val)
- { // insert element at beginning
- c.emplace_back(_STD forward<_Valty>(_Val));
- }
- void swap(_Myt&& _Right)
- { // exchange contents with movable _Right
- c.swap(_STD move(_Right.c));
- }
- bool empty() const
- { // test if stack is empty
- return (c.empty());
- }
- size_type size() const
- { // test length of stack
- return (c.size());
- }
- reference top()
- { // return last element of mutable stack
- return (c.back());
- }
- const_reference top() const
- { // return last element of nonmutable stack
- return (c.back());
- }
- void push(const value_type& _Val)
- { // insert element at end
- c.push_back(_Val);
- }
- void pop()
- { // erase last element
- c.pop_back();
- }
- const _Container& _Get_container() const
- { // get reference to container
- return (c);
- }
- void swap(_Myt& _Right)
- { // exchange contents with _Right
- c.swap(_Right.c);
- }
- protected:
- _Container c; // the underlying container
- };
template<class _Ty,
class _Container = deque<_Ty> >
class stack
{ // LIFO queue implemented with a container
public:
typedef stack<_Ty, _Container> _Myt;
typedef _Container container_type;
typedef typename _Container::value_type value_type;
typedef typename _Container::size_type size_type;
typedef typename _Container::reference reference;
typedef typename _Container::const_reference const_reference;
stack()
: c()
{ // construct with empty container
}
stack(const _Myt& _Right)
: c(_Right.c)
{ // construct by copying _Right
}
explicit stack(const _Container& _Cont)
: c(_Cont)
{ // construct by copying specified container
}
_Myt& operator=(const _Myt& _Right)
{ // assign by copying _Right
c = _Right.c;
return (*this);
}
stack(_Myt&& _Right)
: c(_STD move(_Right.c))
{ // construct by moving _Right
}
explicit stack(_Container&& _Cont)
: c(_STD move(_Cont))
{ // construct by copying specified container
}
_Myt& operator=(_Myt&& _Right)
{ // assign by moving _Right
c = _STD move(_Right.c);
return (*this);
}
void push(value_type&& _Val)
{ // insert element at beginning
c.push_back(_STD move(_Val));
}
template<class _Valty>
void emplace(_Valty&& _Val)
{ // insert element at beginning
c.emplace_back(_STD forward<_Valty>(_Val));
}
void swap(_Myt&& _Right)
{ // exchange contents with movable _Right
c.swap(_STD move(_Right.c));
}
bool empty() const
{ // test if stack is empty
return (c.empty());
}
size_type size() const
{ // test length of stack
return (c.size());
}
reference top()
{ // return last element of mutable stack
return (c.back());
}
const_reference top() const
{ // return last element of nonmutable stack
return (c.back());
}
void push(const value_type& _Val)
{ // insert element at end
c.push_back(_Val);
}
void pop()
{ // erase last element
c.pop_back();
}
const _Container& _Get_container() const
{ // get reference to container
return (c);
}
void swap(_Myt& _Right)
{ // exchange contents with _Right
c.swap(_Right.c);
}
protected:
_Container c; // the underlying container
};
从中我们可以清楚的看到:栈在默认情况下,是基于deque实现的,它使用封装的顺序容器的操作来实现的自己的操作。相信里面的大部分内容我们都能看懂个大概。