1.deque
deque和vector的最大差异在于deque允许在常数时间内对首端进行元素的插入和删除操作。而且deque没有容量的观念,因为它是动态地以分段连续空间组合而成的,随时可以增加一段新的空间并链接起来。像vector那样因旧空间不足而重新配置一块更大空间的情况在deque里是不会发生的。虽然deque也提供Random Access Iterator,但它的迭代器并不是普通指针,这影响了很多操作的效率。
(1)deque的map
deque在逻辑上是连续空间,但实际上它是由一段一段的定量连续空间构成。一旦有必要在deuqe的前端或尾端增加新空间,便配置一段定量的连续空间,串接在整个deque的头端或尾端。deque的最大任务,就是在这些分段的定量连续空间上,维护其整体连续的假象,并提供随机存取的接口。这样避免了vector“重新申请内存、复制、释放”的轮回,代价则是复杂的迭代器架构。
deque采用一块所谓的map作为主控。这里所谓map是一小块连续空间。其中每一个元素都是指针,指向另一段较大的连续空间,称为缓冲区。缓冲区才是deque的存储主体。STL允许我们指定缓冲区大小,如果不指定则默认使用512bytes缓冲区。
deque的定义:
deque里面定义的map对象其实是一个T**,也就是说它是一个指针,所指之物又是一个指针,指向型别为T的一块空间,如下图所示:
(2)deque的迭代器
deque是分段连续空间。维持其“整体连续”假象的任务,落在了迭代器的operator++和operator--两个运算子身上。deque迭代器的数据结构设计如下:
deque的map,缓冲区,迭代器的互相关系如下:
假设现在有一个deque<int>,并令其缓冲区大小为32。经某些操作后deque拥有20个元素。每个int占4字节空间,则每个缓冲区可以存8个int。20个int将占用3个缓冲区,其中第三个缓存区将还剩余16bytes的剩余空间。此时deque的内存使用情况如下:
由于deque特殊的内存结构,当进行指针操作,遇到缓冲区边缘时,要特别注意,有的时候可能需要跳到另外一个缓冲区(实现参考《STL源码解析》148页)。
(3)deque的数据结构
deque除了维护一个先前说过的map的指针外,也维护start,finish两个迭代器,分别指向第一个缓冲区的第一个元素和最后一个缓存区的最后一个元素。此外,它也应该记住当前map的大小。因为map所提供的节点不足,就必须重新配置更大的一块map。
2.stack
stack是一种先进后出的数据结构。它只有一个出口。stack允许新增元素,移除元素,取得顶端元素。但除了顶端外,没有其他任何方法可以存取stack的其它元素。
deque是双向开口的数据结构,以deque为底部结构并封闭其头端开口,便可形成一个stack。
stack是以deque为底部容器完成其所有工作,像stack这种“修改某物接口,形成另一种风貌”的类被称为adapter。其源代码如下: