STL源码剖析(十)序列式容器之deque
文章目录
前面我们说的vector是单向开口,而deque是双向开口,也就是保证常数时间在头部和尾部插入元素
一、deque的数据结构
我们先讨论deque是如何实现双向开口的,也就是可以双向增长?
deque是动态地通过分段连续的空间组成的,也就是说,deque可以随时分配一块内存,连接到原空间的前部或者后部。deque扩容并不会像vector一样,申请一段更大的空间,然后将数据拷贝过去,再释放原内存
deque为了管理这些分段连续的内存空间,使用了一个中控器,中控器是实际是一个指针数组,被使用的元素指向一段连续空间,如下所示
下面我们来看看deque的数据结构
template <class T, class Alloc = alloc, size_t BufSiz = 0>
class deque {
typedef T value_type;
typedef value_type* pointer;
typedef pointer* map_pointer;
...
typedef __deque_iterator<T, T&, T*> iterator;
...
protected: // Data members
/* 迭代器 */
iterator start;
iterator finish;
/* 中控器 */
map_pointer map;
size_type map_size;
...
};
可以看到,deque中有一个map
和map_size
,它们维护着deque的中控器。map
其实是一个指针数组,其数组项为value_type*
类型,指向一块连续的缓存区,map_size
表示该数组项元素个数
另外还有两个迭代器start
和finish
,start
指向首元素,finish
指向最后一个元素的下一个元素
deque提供random_access_iterator_tag
类型的迭代器,而deque内部并不是连续的内存,它只是对外连续,内部是分段连续,所以deque的迭代器为了实现这样的功能,较为复杂
deque内部的数据结构可以用以下图表示
还有一个问题就是,deque的每个缓存区有多大呢?
可以通过类模板中的BufSiz
指定,默认情况下是512个字节
二、deque的迭代器
下面再来分析deque的迭代器,它实现的是一个random_access_iterator_tag
类型的迭代器,而deque内部并不是连续的,所以它的实现较为复杂
它的定义如下
template <class T, class Ref, class Ptr>
struct __deque_iterator {
/* 满足STL迭代器的设计规范 */
typedef random_access_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;
T* cur; //缓存区当前元素
T* first; //缓存区开头
T* last; //缓存区结尾
map_pointer node; //指向中控器的一个节点
...
};
deque的迭代器中有四个变量,cur
指向缓存区的当前元素,first
指向缓存区开头,last
指向缓存区结尾,node
指向中控器的某个节点,如下图所示
所以begin和finish迭代器如下图所示
看完迭代器的数据结构,再来看看它的操作
operator++
self& operator++() {
++cur; //cur指针指向下一个
if (cur == last) { //如果到缓存区结尾
set_node(node + 1); //跳到下一个缓存器
cur = first; //缓存区的第一个节点
}
return *this; //返回操作后的迭代器
}
首先cur指针指向下一个元素,如果到达缓存区的结尾,那么就跳到下一个缓存区,再指向该缓存区的第一个元素
set_node
用于跳转缓存区,我们之后再讨论
operator–