deque是双向开口的,可以在头部插入/删除,也可以在尾部插入/删除,而且都是常数时间的复杂度
而这得益于deque的其结构,deque是由多段线型空间组合而成,可以随时增加一段新的空间并连接起来
因此可以把deque看作是一个链表,而每一个链表节点则是一个数组
虽然deque也提供Random Access Iterator,但是这并不是普通的指针,可以的话尽量使用vector
deque的中控器
deque由于结构并不是真正的线形空间,为了维护其整体连续的假象,并提供随机存取的接口,因此需要使用一块map作为中控器
其中map的每一个元素指向一个节点(一块连续的空间),我们称这块空间为缓冲区
1 | template <class _Tp, class _Alloc> |
deque的迭代器
1 | template <class _Tp, class _Ref, class _Ptr> |
迭代器所进行++和—的操作,需要进行边界判断,判断是否能到达下一个/上一个缓冲区
这个行为依赖于_M_set_node
1 | void _M_set_node(_Map_pointer __new_node) { |
deque的内存管理
deque的结构,使其需要管理map以及缓冲区
当新建缓冲区的时候,不仅需要构建一块缓冲区,还要往map中构造一个指针,而这些操作由以下函数完成
1 |
|
安排deque的结构
1 | template <class _Tp, class _Alloc> |
按照初始化的顺序,首先是通过_M_allocate_map()
来初始化map,随后在通过_M_create_nodes()
来初始化缓冲区,最后再设置start,finish
等变量的值
初始化map
1 | _Tp** _M_allocate_map(size_t __n) |
从源码可以看出,只是单纯地分配一大块内存而已,其大小为
1 | size_t __num_nodes = |
因为map管理的是缓冲区,然后每个缓冲区都可以存放一定的nodes,因此大小为node总数/每个缓冲区的大小+1
初始化缓冲区
1 | _Tp** __nstart = _M_map + (_M_map_size - __num_nodes) / 2; |
让nstart
和nfinish
指向map的中间部分,因为deque是双向开口的,因此要保证向前和向后扩充的容量是一样的,每个节点对应一个缓冲区
元素操作
push_front
1 | void push_front(const value_type& __t) { |
pop_front
1 | void pop_front() { |
insert
1 | iterator insert(iterator position, const value_type& __x) { |