deque是一个双向开口的容器,在头尾两端进行元素的插入跟删除操作都有理想的时间复杂度。

deque使用的是分段连续线性空间,它维护一个指针数组(T** map),其中每个指针指向一块连续线性空间。

(map左右两边一般留有剩余空间,用于前后插入元素,具体下面可以看到其实现)

根据上图,可以了解到deque的迭代器的基本定义。

 template <class T, class Ref, class Ptr, size_t BufSiz>
struct __deque_iterator {
// 基本型别的定义
typedef __deque_iterator<T, T&, T*, BufSiz> iterator;
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;
typedef __deque_iterator self; // 缓冲区的大小
tatic size_t buffer_size() { ... } // 主要维护的三个指针
T* cur; // 指向当前元素
T* first; // 指向当前缓冲区的头
T* last; // 指向当前缓冲区的尾 map_pointer node; // 指向当前缓冲区在map中的位置
// ...
};

deque的实现基本都是依赖于其迭代器的实现(主要是各种操作符的重载)

 // 用于跳到下一个缓冲区
void set_node(map_pointer new_node) {
node = new_node;
first = *new_node;
last = first + difference_type(buffer_size());
} reference operator*() const { return *cur; }
pointer operator->() const { return &(operator*()); } self& operator++() {
++cur;
if (cur == last) { // 到达缓冲区尾端
set_node(node + );
cur = first;
}
return *this;
} self& operator--() {
if (cur == first) { // 已到达缓冲区头端
set_node(node - );
cur = last;
}
--cur;
return *this;
} // 迭代器之间的距离(相隔多少个元素)
difference_type operator-(const self& x) const {
return difference_type(buffer_size()) * (node - x.node - ) +
(cur - first) + (x.last - x.cur);
}

该迭代器还重载了operator+=、operator+、operator-=、operator-(difference_type)等,

都是通过set_node()跟调整cur、first、last、node成员来实现。同时重载的operator[]使用operator+来进行随机存取。

 self& operator+=(difference_type n) {
difference_type offset = n + (cur - first);
if (offset >= && offset < difference_type(buffer_size()))
cur += n;
else {
// 目标在不同的缓冲区
difference_type node_offset =
offset > ? offset / difference_type(buffer_size())
: -difference_type((-offset - ) / buffer_size()) - ;
// 跳到相应的缓冲区
set_node(node + node_offset);
// 调整cur指针
cur = first + (offset - node_offset * difference_type(buffer_size()));
}
return *this;
} // 下面的都直接或间接的调用operator+=
self operator+(difference_type n) const {
self tmp = *this;
return tmp += n;
} self& operator-=(difference_type n) { return *this += -n; } self operator-(difference_type n) const {
self tmp = *this;
return tmp -= n;
} reference operator[](difference_type n) const { return *(*this + n); }

有了__deque_iterator,deque的基本实现就比较简单了(主要维护start、finish这两个迭代器)

下面是deque的基本定义

 template <class T, class Alloc = alloc, size_t BufSiz = >
class deque {
public:
typedef T value_type;
typedef value_type* pointer;
typedef size_t size_type;
typedef pointer* map_pointer;
public:
typedef __deque_iterator<T, T&, T*, BufSiz> iterator;
protected:
iterator start; // 第一个节点
iterator finish; // 最后一个结点 map_pointer map;
size_type map_size;
public:
iterator begin() { return start; }
iterator end() { return finish; } reference operator[](size_type n) { return start[difference_type(n)]; } // 调用迭代器重载的operator[] // ...
}

deque的constructor会调用create_map_and_nodes()来初始化map

 // 每次配置一个元素大小的配置器
typedef simple_alloc<value_type, Alloc> data_allocator;
// 每次配置一个指针大小的配置器
typedef simple_alloc<pointer, Alloc> map_allocator; template <class T, class Alloc, size_t BufSize>
void deque<T, Alloc, BufSize>::create_map_and_nodes(size_type num_elements) {
// 需要分配的结点数 如果为能整除 则多分配多一个结点
size_type num_nodes = num_elements / buffer_size() + ; // 分配结点内存 (前后预留一个 用于扩充)
map_size = max(initial_map_size(), num_nodes + );
map = map_allocator::allocate(map_size); // 将需要分配缓冲区的结点放在map的中间
map_pointer nstart = map + (map_size - num_nodes) / ;
map_pointer nfinish = nstart + num_nodes - ; map_pointer cur;
// 为了简化 去掉了异常处理的代码
for (cur = nstart; cur <= nfinish; ++cur)
*cur = allocate_node(); // 为每个结点分配缓冲区
} // 设置start、finish指针
start.set_node(nstart);
finish.set_node(nfinish);
start.cur = start.first;
finish.cur = finish.first + num_elements % buffer_size();
}

下面就剩下插入跟删除元素的实现了,首先看看关于push_front()的操作的实现。

 void push_front(const value_type& t) {
if (start.cur != start.first) { // 第一缓冲区还有容量
construct(start.cur - , t);
--start.cur;
}
else
push_front_aux(t);
} // 如果第一缓冲区容量不足会调用这个函数来配置新的缓冲区
template <class T, class Alloc, size_t BufSize>
void deque<T, Alloc, BufSize>::push_front_aux(const value_type& t) {
value_type t_copy = t;
reserve_map_at_front(); // 可能导致map的重新整治
*(start.node - ) = allocate_node();
start.set_node(start.node - );
start.cur = start.last - ;
construct(start.cur, t_copy);
} // 根据map前面为分配的结点数量来判断是否需要重新整治
void reserve_map_at_front (size_type nodes_to_add = ) {
if (nodes_to_add > start.node - map)
reallocate_map(nodes_to_add, true);
}

上面留下的reallocate_map函数执行如下功能:

1.如果map中空闲指针足够多,则将已分配的结点移到map的中间。

2.否则重新分配一个map,将旧的map释放,把已分配的结点移到new_map的中间。

然后调整start跟finish迭代器。

然后是pop_front()的实现

 void pop_front() {
if (start.cur != start.last - ) {
destroy(start.cur);
++start.cur;
}
else
pop_front_aux();
} // 当前缓冲区只剩一个元素
template <class T, class Alloc, size_t BufSize>
void deque<T, Alloc, BufSize>::pop_front_aux() {
destroy(start.cur);
deallocate_node(start.first); // 释放该缓冲区
start.set_node(start.node + );
start.cur = start.first;
}

而push_back()跟pop_back()的实现跟上面的大同小异。

最后看看erase()跟insert()的实现

 iterator erase(iterator pos) {
iterator next = pos;
++next;
difference_type index = pos - start; // 迭代器的operator-
if (index < (size() >> )) { // 如果清除点之前的元素比较少
// 将清除点之前的所有元素后移一位 然后删除第一个元素
copy_backward(start, pos, next); // 利用了迭代器的operator--
pop_front();
}
else { // 如果清除点之后的元素比较少
// 将清除点之后的所有元素前移一位 然后删除最后一个元素
copy(next, finish, pos); // 利用了迭代器的operator++
pop_back();
}
return start + index;
}
 iterator insert(iterator position, const value_type& x) {
if (position.cur == start.cur) {
// 插入位置为begin()
push_front(x);
return start;
}
else if (position.cur == finish.cur) {
// 插入位置为end()
push_back(x);
iterator tmp = finish;
--tmp;
return tmp;
}
else {
// 如果插入位置是在(begin(), end())
return insert_aux(position, x);
}
} // insert_aux()跟erase()实现类似
// 调用copy()或者copy_backward()将元素前移或者后移
// 然后修改原来位置的值

STL源码剖析(deque)的更多相关文章

  1. STL"源码"剖析-重点知识总结

    STL是C++重要的组件之一,大学时看过<STL源码剖析>这本书,这几天复习了一下,总结出以下LZ认为比较重要的知识点,内容有点略多 :) 1.STL概述 STL提供六大组件,彼此可以组合 ...

  2. 【转载】STL"源码"剖析-重点知识总结

    原文:STL"源码"剖析-重点知识总结 STL是C++重要的组件之一,大学时看过<STL源码剖析>这本书,这几天复习了一下,总结出以下LZ认为比较重要的知识点,内容有点 ...

  3. STL源码剖析 迭代器(iterator)概念与编程技法(三)

    1 STL迭代器原理 1.1  迭代器(iterator)是一中检查容器内元素并遍历元素的数据类型,STL设计的精髓在于,把容器(Containers)和算法(Algorithms)分开,而迭代器(i ...

  4. STL"源码"剖析

    STL"源码"剖析-重点知识总结   STL是C++重要的组件之一,大学时看过<STL源码剖析>这本书,这几天复习了一下,总结出以下LZ认为比较重要的知识点,内容有点略 ...

  5. 《STL源码剖析》相关面试题总结

    原文链接:http://www.cnblogs.com/raichen/p/5817158.html 一.STL简介 STL提供六大组件,彼此可以组合套用: 容器容器就是各种数据结构,我就不多说,看看 ...

  6. STL源码剖析之序列式容器

    最近由于找工作需要,准备深入学习一下STL源码,我看的是侯捷所著的<STL源码剖析>.之所以看这本书主要是由于我过去曾经接触过一些台湾人,我一直觉得台湾人非常不错(这里不涉及任何政治,仅限 ...

  7. 《STL源码剖析》读书笔记

    转载:https://www.cnblogs.com/xiaoyi115/p/3721922.html 直接逼入正题. Standard Template Library简称STL.STL可分为容器( ...

  8. STL源码剖析之组件

    本篇文章开始,进行STL源码剖析的一些知识点,后续系列笔记全是参照<STL源码剖析>进行学习记录的 STL在现在的大部分项目中,实用性已经没有Boost库好了,毕竟STL中仅仅提供了一些容 ...

  9. 面试题总结(三)、《STL源码剖析》相关面试题总结

    声明:本文主要探讨与STL实现相关的面试题,主要参考侯捷的<STL源码剖析>,每一个知识点讨论力求简洁,便于记忆,但讨论深度有限,如要深入研究可点击参考链接,希望对正在找工作的同学有点帮助 ...

随机推荐

  1. 德州扑克AI

    德州扑克: 1:outs数,就是所听的牌的数量. 例子: 1:听顺子 4567 outs数就是8,能够成顺子的牌为3和8. 5689 outs数就是4,能够成顺子的牌只有7. 2:听同花     35 ...

  2. 安装Windows渗透环境工具--PentestBox

    PentestBox不同于运行在虚拟机或者双启动环境的Linux渗透测试发行版. 它打包了所有的安全工具,并且可以在Windows系统中原生地运行,有效地降低了对虚拟机或者双启动环境的需求. 下载地址 ...

  3. Oracle concat

    如果要进行多个字符串的拼接的话,可以使用多个CONCAT()函数嵌套使用,上面的SQL可以如下改写:SELECT CONCAT(CONCAT(CONCAT('工号为',FNumber),'的员工姓名为 ...

  4. Linux基础系列-Day8

    Shell编程基础 Shell介绍 Shell俗称壳(用来区别于核),是指“提供使用者使用界面”的软件(命令解析器).它类似于windows下的的cmd.exe.它接收用户命令,然后调用相应的应用程序 ...

  5. BestCoder Round #65 (ZYB's Biology)

    ZYB's Biology Accepts: 848 Submissions: 1199 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 13 ...

  6. oracle latch工作原理

    latch是一种轻量级用于保护oracle共享内存结构,用户并发操作一致性的串行化锁定机制,如SGA中,各种数据被反复从磁盘读取到内存,又被重新写回到磁盘上,如果有并发用户做相同的事情,oracle必 ...

  7. Codeforces Round #453 ( Div. 2) Editorial ABCD

    A. Visiting a Friend time limit per test 1 second memory limit per test 256 megabytes input standard ...

  8. 【最短路】NOIP模拟赛 虫洞

    虫洞 [题目描述] N个虫洞,M条单向跃迁路径.从一个虫洞沿跃迁路径到另一个虫洞需要消耗一定量的燃料和1单位时间.虫洞有白洞和黑洞之分.设一条跃迁路径两端的虫洞质量差为delta. 1. 从白洞跃迁到 ...

  9. 【OpenJudge9277】【递推】Logs Stacking堆木头

    Logs Stacking堆木头 总时间限制: 1000ms 内存限制: 131072kB [描述] Daxinganling produces a lot of timber. Before loa ...

  10. 使用webclient上传下载实例

    转载:http://blog.csdn.net/kevonz/article/details/5078432 using System; using System.Collections.Generi ...