deque容器的结构就是多个指针串接起来的多块缓冲区,其中的指针也保存在一块缓冲区中,源码中称其为map(不是容器map),借此实现连续空间的假象,说起来其结构比真正的连续空间vector复杂多了,好处就是不用特意维持一块真正的连续空间(想想如果数据量超级大,当空间满了后需要继续添加元素,就得整个进行内存拷贝,可怕),当需要进行内存移动时,可以减少移动的数据量(后边看insert就知道),借用stl源码剖析的图,deque结构如下,就不打了。
实际上进行insert操作,如果不是再首部或者尾部插入都会造成内存拷贝,尤其是当插入的位置在容器中间时,就意味着需要进行一半数据量的内存拷贝,而且相比于vector,deque维持这样的 “连续” 空间需要付出更大的代价,和vector一样,在需要大量使用插入删除的操作场景下,优先考虑其他容器。
deque的insert有多个重载版本,这里就记录源码剖析里的例子,其它的大同小异
iterator insert(iterator position, const value_type& x) { if (position.cur == start.cur) { /*插入最前端*/ push_front(x); return start; } else if (position.cur == finish.cur) { /*插入最尾端*/ push_back(x); iterator tmp = finish; --tmp; return tmp; } else {
/*其它位置*/ return insert_aux(position, x); } }
当插入最前端或者最后端是调用push_front或者push_back函数实现。
这两个函数的实现比较简单,这里列出push_front的实现 void push_front(const value_type& t) {
void push_front(const value_type& t) { if (start.cur != start.first) { /*第一块缓冲区还有剩余位置*/ /*构造start.cur的前一个位置*/ construct(start.cur - 1, t); /*start.cur前移一步*/ --start.cur; } else push_front_aux(t); }
// Called only if start.cur == start.first.
//第一个缓冲区已经满了
template <class T, class Alloc, size_t BufSize> void deque<T, Alloc, BufSize>::push_front_aux(const value_type& t) { /*记下value*/ value_type t_copy = t; /*判断是否需要更换map(deque要维持其首尾两边有可用空间)*/ /*想象一下也知道,更换map就是把挂着的给个缓冲区指针复制到另一段空间去,然后更改前后迭代器的指向*/ reserve_map_at_front(); /*首节点前一个位置构造一条新的缓冲区*/ *(start.node - 1) = allocate_node(); __STL_TRY { /*更换首节点位置(start是deque的迭代器,永远指向第一个节点)*/ start.set_node(start.node - 1); /*设置cur为缓冲区最后一个位置(前移了一步)*/ start.cur = start.last - 1; /*新节点构造初值*/ construct(start.cur, t_copy); } # ifdef __STL_USE_EXCEPTIONS catch(...) { /*异常回滚操作*/ start.set_node(start.node + 1); start.cur = start.first; deallocate_node(*(start.node - 1)); throw; } # endif /* __STL_USE_EXCEPTIONS */ }
罗里吧嗦将了一堆,终于到正题了insert_aux(position, x)
这个函数的作用就是向迭代器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*/ value_type x_copy = x; /*根据插入点前后元素数量决定做前移或者后移操作*/ /*使得实现少的内存拷贝*/ if (index < size() / 2) { /*插入点前的元素个数较少*/ /*最前端加一个元素,以此时的front()为初值*/ push_front(front()); /*此时的start赋值给front1,记住push_front后start会前移*/ /*下边四行将形成 start front1 front2从前往后的依次排序*/ iterator front1 = start; ++front1; iterator front2 = front1; ++front2; /*重设pos位置(前移了一个元素位置)*/ pos = start + index; /*用pos1指向原pos的位置*/ iterator pos1 = pos; ++pos1; /*将区间[front2,pos1)整个区间前移一步*/ copy(front2, pos1, front1); } else { /*插入点前的元素较多,所以选择插入点后的元素整体后移一步*/ push_back(back()); iterator back1 = finish; --back1; iterator back2 = back1; --back2; pos = start + index; /*逆向拷贝(后移)*/ copy_backward(pos, back2, back1); } /*安插点设置值*/ /*空出来的这个位置赋值为value*/ *pos = x_copy; return pos; }