1.总体概览
- 双向开口的连续线性空间
- 头尾均可以常数时间插入和删除
- 空间是动态地以分段连续空间组合而成,随时可以增加一段新的空间并链接起来,不用像vector那样重新分配新空间再拷贝
- deque提供Random Access Iterator 但是其迭代器不是普通指针,复杂度较之于vector会高出很多,所以一般使用vector而不是deque
2.deque的中控器(map)
- 采用一块所谓的
map(并非STL的map容器)
,map是一小块连续空间,每个元素都是指针,指向一段连续空间(缓冲区
) - 缓冲区才是数据存储的主体
- 允许指定缓冲区大小,当默认值0表示缓冲区使用512字节
3.迭代器
- 需要指出分段连续空间在哪,并判断是否处于缓冲器边缘,并能够跳跃到另一个缓冲区
- 迭代器中含有三个
T*
指针,cur,first,last
指的是迭代器所指的缓冲区中的当前元素、头、尾 - 此外还含有一个
map_pointer
类型的指针(可以看为T**),指向map指针数组的一项,代表当前迭代器所属的缓冲区
3.1buffer_size
,主要调用全局函数__deque_buf_size
,获取缓冲区的大小
3.2 set_node
跳跃一个缓冲区
void set_node(map_pointer new_node){
node=new_node;
first=*new_node;
last=first+(difference_type)buffer_size();
}
3.3 重载运算符-
,两个迭代器相减
-
除去两个操作数所在的缓冲区个数*buf_size
-
尾部部分 cur-first
-
头部部分 x.last-x.cur
3.4 随机存取,跳跃n个位置
4.deque数据结构
deque内部除了一个指向map的指针,还有两个指针start end
,分别指向第一个缓冲区的第一个元素
和最后一个缓冲区最后一个元素
此外,也需要记住map的大小,再map空间不足时,需要扩展map(重新分配空间,并且,map是从中间开始,向两侧扩散使用)
5.deque的内存管理
deque有两个空间配置器,一个用来分配元素的存储空间(即缓冲区),另一个用来分配map的空间
typedef simple_alloc<value_type,Alloc> data_allocator;
typedef simple_alloc<pointer,Alloc> map_allocator;
5.1 create_map_and_nodes(size_type num_elements)
- 求出需要都少个缓冲区
size_type num_nodes=num_elements/buffer_size()+1;
- 一个map最少管理8个节点,最多是“所需节点数+2”
map_size = max(initial_map_size(),num_nodes+2);
- 配置map的空间
- 使得map使用的部分处于整体的中间部位,利于之后的扩充、
map_pointer nstart =map+(map_size-num_nodes)/2;
map_pointer nfinish = nstart+num_nodes-1;
- 为map中每个现用节点配置空间,即让其指向缓冲区,也即开始配置缓冲区的空间
for(cur = nstart;cur<=nfinish;++cur)
*cur=allocate_node()
- 调整start和end的内容
完整代码如下:
5.2 填入初值
主要是调用create_map_and_nodes
和uninitialized_fill
5.3 构造函数
6.deque的操作
6.1 push_back
- 最后的缓冲区还有>=2的可用空间,则在这个缓冲区的cur位置构造新元素(注意:push_back中,cur表示下一个可用位置)
- 若只有一个可用空间,则调用
push_back_aux
可能涉及到map的调整
6.2 push_back_aux
reverse_map_at_back
以及后面的reverse_map_at_front
都是重新调整map的结构
6.3 push_front
- 往前插时与往后插不同,cur表示的是第一个元素,往前插是需要在++cur上构造新的元素
- 故,push_front只需要判断是否还有可用空间即可,若无可用空间,则使用
push_front_aux
6.4 push_front_aux
6.5 reverse_map_at_back 和reverse_map_at_front
都是调用reallocate_map完成,注意finish指向的是一个有效的节点,其代表最后一个缓冲区
6.6 reallocate_map
- 第一种情况,造成空间map空间不足的原因可能是因为一直往一个方向插入数据,使得map的分布没有处在中间位置,所以可以通过内部移动来解决。判定条件为:
size_type old_num_nodes=finish.node-start.node+1;
size_type new_num_node=old_num_nodes+nodes_to_add;
if(map_size>2*new_num_nodes)
//内部调整
- 第二种情况,重新分配更大的内存,new_map_size=map_size+max(map_size,node_to_add)+2;
6.7 clear
恢复最初状态,保留一个缓冲区
6.8 insert,支持任意位置插入,只是除首尾外,其效率较低
- 判断位置是否为首/尾,真,则直接交由push_back和push_front处理
- 否则,调用
insert_aux
上述代码简而言之为:若前半部分元素数量少,则选择移动前半部分来完成插入,首先,复制第一个元素,push_front(front()),然后移动其他元素,空出一个位置,最后在该位置上填入要插入的元素;若后半部分元素少,先复制最后一个元素,push_back(back()),然后逆序复制其他元素,空出一个位置,最后在该位置填入要插入的值。