在详解什么是容器适配器之前,初学者首先要理解适配器的含义。
其实,容器适配器中的“适配器”,和生活中常见的电源适配器中“适配器”的含义非常接近。我们知道,无论是电脑、手机还是其它电器,充电时都无法直接使用 220V 的交流电,为了方便用户使用,各个电器厂商都会提供一个适用于自己产品的电源线,它可以将 220V 的交流电转换成适合电器使用的低压直流电。
从用户的角度看,电源线扮演的角色就是将原本不适用的交流电变得适用,因此其又被称为电源适配器。
简单理解 容器适配器, 就是将不适用的序列式容器 (包括vector, deque, list ) 变得适用,
即通过封装某个序列式容器,并重新组合该容器中包含的成员函数, 使其满足某些特定场景的需要;
举一个例子,假设一个代码模块 A,它的构成如下所示:
class A{
public:
void f1(){}
void f2(){}
void f3(){}
void f4(){}
};
现在我们需要设计一个模板 B,但发现,其实只需要组合一下模块 A 中的 f1()、f2()、f3(),就可以实现模板 B 需要的功能。其中 f1() 单独使用即可,而 f2() 和 f3() 需要组合起来使用,如下所示:
class B{
private:
A * a;
public:
void g1(){
a->f1();
}
void g2(){
a->f2();
a->f3();
}
};
可以看到,就如同是电源适配器将不适用的交流电变得适用一样,
模板 B 将不适合直接拿来用的模板 A 变得适用了,因此我们可以将模板 B 称为 B 适配器。
容器适配器的底层实现和模板 A、B 的关系是完全相同的。
容器适配器本质上还是容器,只不过此容器模板类的实现,利用了大量其它基础容器模板类中已经写好的成员函数。当然,如果必要的话,容器适配器中也可以自创新的成员函数。
注意, STL 中的容器适配器, 其内部使用的基础容器并不是固定的, 用户可以在满足特定条件的多个基础容器中根据需求 选择;
各个容器适配器,所使用的默认基础容器, 以及可以供用户选择的基础容器, 如表所示
表 1 STL 容器适配器及其基础容器
容器适配器 | 基础容器筛选条件 | 默认使用的基础容器 |
---|---|---|
stack | 基础容器需包含以下成员函数: empty() , size() ,back(), push_back(), pop_back()满足条件的基础容器有 vector、deque、list。 | deque |
queue | 基础容器需包含以下成员函数:empty(),size(),front(),back() ,push_back(), pop_front()满足条件的基础容器有 deque、list。 | deque |
priority_queue | 基础容器需包含以下成员函数:empty(),size(),front(),push_back(),pop_back(), 满足条件的基础容器有vector、deque。 | vector |
不同场景下, 不同的序列式容器其底层采用的数据结构不同,
因此,容器适配器的执行效率也不尽相同;
由以上知识点,
stack 是容器适配器,虽然本质上是容器,但其机理是调用底层的基础容器, 进行改动然后封装成特定功能的容器;
通常情况下,我们使用的是 SGI STL 数据结构中的 stack ;
三个最为普遍的STL版本:
HP STL 其他版本的C++ STL,一般是以HP STL为蓝本实现出来的,HP STL是C++ STL的第一个实现版本,而且开放源代码。
P.J.Plauger STL 由P.J.Plauger参照HP STL实现出来的,被Visual C++编译器所采用,不是开源的。
SGI STL 由Silicon Graphics Computer Systems公司参照HP STL实现,被Linux的C++编译器GCC所采用,SGI STL是开源软件,源码可读性甚高。
接下来介绍的栈和队列也是SGI STL里面的数据结构, 知道了使用版本,才知道对应的底层实现。
栈是以底层容器完成其所有的工作,对外提供统一的接口,底层容器可以 是vector,deque,list ,也就是说我们可以控制使用哪种容器来实现栈的功能。
所以STL中栈被归类为container adapter(容器适配器)。
而 vector, deque, list 的底层实现可以是 数组 或链表;
我们常用的SGI STL,如果没有指定底层实现的话,默认是以deque为缺省情况下栈的低层结构。
deque是一个双向队列,只要封住一段,只开通另一端就可以实现栈的逻辑了。
SGI STL中 队列底层实现缺省情况下一样使用deque实现的。
我们也可以指定vector为栈的底层实现,初始化语句如下:
std::stack<int, std::vector<int> > third;
// 使用vector为底层容器的栈
刚刚讲过栈的特性,对应的队列的情况是一样的。
队列中先进先出的数据结构,同样不允许有遍历行为,不提供迭代器, SGI STL中队列一样是以deque为缺省情况下的底部结构。
也可以指定list 为起底层实现,初始化queue的语句如下:
std::queue<int, std::list<int>> third;
// 定义以list为底层容器的队列
栈提供push 和 pop 等等接口,所有元素必须符合先进后出规则,所以栈不提供走访功能,也不提供迭代器(iterator)。
不像是set 或者map 提供迭代器iterator来遍历所有元素。