一、Deque 深度探索
双向扩充队列
push_front() pop_front() push_back() pop_back()
内存中是分段连续的buffer,每次扩充时,扩充一个固定的 buffer 大小。
deque 本身大小为40个字节=2个 iterator (16) + 一个指针 (4) + 一个整数(4) 内部放做少元素是动态分配获得的,和对象本身没有关系。
模板参数中的 BufSiz 是指每个 buffer 容纳的元素个数,可自定。默认为0时,根据 sz[ sizeof(value_type) ] 的大小,
if sz < 512 则 buffersize = 512 / sz else buffersize = 1
控制中心 map 是一个指针 指向一个指针的数组 vector ,其扩充时也是两倍成长,每次扩充后 copy 原有元素到新内存的中段,时前部和后部后空出,以便于增加 map 中 node 时的操作。
template<class T, class Ref, class Ptr, size_t BufSiz>
struct __deque_iterator {
typedef random_acess_iterator_tag iterator_category;
typedef T value_type;
typedef Ptr pointer;
typedef Ref reference;
typedef size_t size_type;
typedef ptrdiff_t difference_type;
typedef T** map_pointer;
typedef __dequr_iterator self;
T* cur;
T* first;
T* last;
map_pointer node; //指向map中的元素,即是一个指向指针的指针
....
};
在队列前后入队出队时,迭代器指向边界时,会自动修正++与--所指向的地址,有能力跳到下一个缓冲区,以保证表面上看是连续的。
在指定位置安插一个值为 x 的元素 insert(),会智能判断所安插的位置离前端还是后端比较近,从而选择效率高的一端推移,空出一个位置来放入新元素。
没有 sort() 类方法,直接使用 ::sort() STL模板函数。
-
Deque 如何模拟连续空间 — 由 deque iterator 实现
主要实现思想就是定义迭代器的 + - = 等操作,要求能够检查边界,跳到另一个缓存区去。
operator - 得到 A 与 B 之间有多少个元素:
B - A = 每个缓冲区放的元素个数 * AB之间完整的缓存区数量 + 末尾 buffer 的元素量 + 起始 buffer 的元素量
++ 与 -- 的实现,基本都是由 后 ++ 调用 前 ++ ,后 -- 调用 前 -- 。
deque的指针是一种随机访问指针,所以应该支持 +n 操作,重载 += 与 + 操作。-= 可以利用 += 代码实现。
self& operator-= (difference_type n)
{ return *this += -n; }
self operator- (difference_type n) const
{
self tmp = *this;
return tmp -= n;
}
reference operator[] (difference_type n) const
{ return *(*this + n); }
GNU4.9中,deque不允许指定 buffersize ,只提供两个模板参数。
二、Stack 和 Queue
- queue 和 stack 都是基于 deque 实现,(内含一个deque,并限制某些操作)技术上本质为容器适配器。
- 因为只能先进先出或者先进后出,不允许遍历,所以不提供迭代器。 (声明 iterator 时会报错)
其中数据为默认类型为 deque 的容器 c,所有的对 queue 和 stack 的操作都是转调用 c 的类方法去做。
本质上能完成这些转调用方法的容器都可以作为底层容器去实现 queue 。
- list 和 deque 都可以作为底层容器实现 stack 和 queue。
- stack 可以选择 vector 作为底层结构,queue 不可选择 vector 作为底层结构。
在声明 stack 和 queue 时,其底层容器类型 Sequence 作为模板参数,只要未涉及不能实现的相关操作,编译时其实都能通过。