deque源码实现
G2.9版本的deque源码
deque对外呈现出的是一个双向数组,其示意图和代码如下:
- deque内部实际上是由一个个离散的内存段组成的,术语叫做buffer,这些buffer的头指针被保存在一个map(表)中,通过deque的迭代器的行为,就可以对外表现为一个连续的内存段(示意图中的map不是容器来的…不要搞混了,map表可以往左右两边扩充,同时map前后各留了一个node节点,以便扩充可用,map一开始没有像vector一样有capacity(),所以当发现当前容量不够时会立即释放当前内存,重新申请内存)
- 可以看出deque内部包含两个迭代器start和finish,分别指向头尾,除此之外还有一个map指向保存指向buffer的指针的内存段,以及一个表示map长度的map_size。
- 观察deque的迭代器我们可以看出,里面包含四个指针,其中cur,first,last是指示当前buffer中位置的指针,node是指示当前buffer在整个map中位置的指针。cur表示元素在当前buffer中的位置,first和last分别标记该buffer的头尾,方便iterator进行后续的边界判断
- deque初始长度为40字节,因为deque中有2个iterator(包含cur,first,last,node),1个map_pointer,1个size_type(4字节)
- G2.9允许用户自己定义每一个buffer的大小
G4.9版本的deque源码
deque的insert()
iterator insert(iterator position, const value_type& x){
if(position.cur == start.cur){ //如果安插点是deque最前端,那么交给push_front()做插入
push_front(x);
return start;
}
else if(position.cur == finish.cur){ //如果安插点是deque最尾端,那么交给push_back()做插入
push_back(x);
iterator tmp = finish;
--tmp;
return tmp;
}
else{
return insert_aux(position, x);
}
}
template <class T, class Alloc, size_t BufSize>
typename deque<T, Alloc, BufSize>::iterator
deque<T, Alloc, BufSize>::insert_aux(iterator pos, const value_type& x){
difference_type index = pos - start; //安插点之前的元素个数
value_type x_copy = x;
if(index < size() / 2){ //如果安插点之前的元素个数较少
push_front(front()); //在最前端加入与第一元素同值的元素
......
copy(front2, pos1, front1); //元素搬移
}
else{ //安插点之后的元素个数较少
push_back(back()); //在尾端加入与最末元素同值的元素
......
copy_backward(pos, back2, back1); //元素搬移
}
*pos = x_copy; //在安插点设定新值
return pos;
}
- insert()巧妙之处在于先判断插入位置靠近头还是尾,然后将数据进行往前或者往后搬移腾出位置来塞插入的元素。
deque如何模拟连续空间
由于deque中定义了operator[],size()等操作,所以用户在使用deque的时候,感觉仿佛是在对一段连续空间进行操作:
reference operator[](size_type n)
{ return start[difference_type(n)]; }
reference front()
{ return *start; }
reference back()
{
iterator tmp = finish;
tmp--;
return *tmp;
}
size_type size()const
{ return finish-start; }
size_type empty()const
{ return finish == start; }
deque模拟连续空间有赖于deque iterator对于运算符的重载:
reference operator*()const
{ return *cur; }
pointer operator->()const
{ return &(operatro*()); }
difference_type operator-(const self& x) const
{
return difference_type(buffer_size()) * (node - x.node - 1) + //首尾buffers之间的buffers数量
(cur - first) + //末尾buffer的元素量
(x.last - x.cur); //起始buffer的元素量
}
注意事项
deque的迭代器类型以及相关操作的时间复杂度
随机访问迭代器
随机访问每个元素 常量时间
末尾插入删除元素 常量时间
中间或开头删插元素 线性时间
deque的迭代器失效问题
插入元素
- 头尾
可能使所有元素的迭代器失效(buffer中一段连续空间满,需释放map内存,并重新分配map和buffer的内存时),也可能指向头或尾的元素的迭代器将失效,指向其他元素的迭代器,但指针、引用仍有效(buffer的原有空间并未发生改变) - 其他位置
迭代器、指针、引用失效
删除元素
- 头尾
指向头或尾的元素的迭代器将失效,指向其他元素的迭代器、指针、引用仍然有效(buffer的原有空间并未发生改变) - 其他位置
迭代器、指针、引用失效