本文涉及到 SGI STL 源码的文件有deque、stl_deque.h、deque.h、stack、stl_stack.h、queue、stl_queue.h 等几个文件。
1. deque 概述
前面分别介绍了连续式存储的序列容器vector和以节点为单位链接起来的非连续存储的序列容器list,这两者各有优缺点,而且刚好是优缺互补的,那么何不将二者结合利用对方的优点来弥补己方的不足呢,于是这就有了强大的deque。
没错,与我们在数据结构中学到的固定连续空间的双端队列不同,STL中的deque是分段连续的空间通过list链接而成的序列容器,它结合了vector与list的存储特性,但与vector和list都不同的是deque只能在首部或尾部进行插入和删除操作,这个限制在一定程度上简化了deque实现的难度。由于使用分段连续空间链接的方式,所以deque不存在vector那样“因旧空间不足而重新配置新的更大的空间,然后复制元素,再释放原空间”的情形,也不会有list那样每次都只配置一个元素的空间而导致时间性能和空间的利用率低下。
2. deque 的数据结构
deque由一段一段连续空间串接而成,一旦有必要在deque的头部或尾端增加新的空间,便配置一段定量连续的空间,串接在deque的头部或尾端。deque的最大任务,就是在这些分段连续的空间上维护其整体连续的假象,并提供随机存取的接口。deque采用一块所谓的map(注意:不是STL中map容器,而是类似于vector)作为主控(为什么不使用list呢?),这块map是一个连续空间,其中每个元素都是一个指针,指向一段连续的空间,称为缓冲区,它才是deque的真正存储空间。SGI中允许指定缓冲区的大小,默认是512字节。除此之外,还有start和finish两个指针,分别指向第一个缓冲区的第一个元素和最后一个缓冲区的最后一个元素。其数据结构如下:
cpp
inline size_t __deque_buf_size(size_t __size) { // 计算缓冲区的大小
return __size < 512 ? size_t(512 / __size) : size_t(1);
}
template <class _Tp, class _Alloc> class _Deque_base {
protected:
_Tp** _M_map; // 指向缓冲区的指针数组首地址
size_t _M_map_size; // 指向缓冲区的指针数组的大小
iterator _M_start; // 指向第一个缓冲区的第一个元素
iterator _M_finish; // 指向最后一个缓冲区的最后一个元素
};
class deque : protected _Deque_base<_Tp, _Alloc> {
protected: // Internal typedefs
typedef pointer* _Map_pointer;
static size_t _S_buffer_size() { return __deque_buf_size(sizeof(_Tp)); }
};

