STL源码分析之deque,双端队列(一)

前言

deque 双端队列,支持从两头push和pop数据,比一般的队列功能更加强大。也是一个常用的数据结构。本系列博客将来分析deque的实现源码。这个deque比之前看的vector复杂了一些。

基础函数

inline size_t __deque_buf_size(size_t n, size_t sz)
{
  return n != 0 ? n : (sz < 512 ? size_t(512 / sz) : size_t(1));
}

deque的实现比较有意思,它使用一些些内存buf来让队列能够动态的生长。
里面的buf其实就是下面说的一个节点node所指向的东西。这个buf含有若干个元素,那么一个buf到底含有多少个元素嘞?
上面的函数就告诉我们答案了。
如果每个元素的大小 sz 小于 512个字节,那么 一个buf放512 /sz 个元素,否则一个buf就放一个元素。举个例子 ,假设这个一个int的双端队列,那么 每个buf占512字节,每个buf可以放128个int.
同时 这说明了什么呢? 说明deque在给每个buf申请内存的时候最少也会申请512个字节的。
它的内部结构是酱紫的:
这里写图片描述
那个T数组就是上面这个函数所说的Buf.

迭代器管理类

deque首先使用了一个专门的迭代器类__deque_iterator 来方便自己内部的实现和内存的管理。其完整代码结构如下:
注意这是个struct,因此默认的访问控制关键字是public

#ifndef __STL_NON_TYPE_TMPL_PARAM_BUG
template <class T, class Ref, class Ptr, size_t BufSiz>
struct __deque_iterator {
  typedef __deque_iterator<T, T&, T*, BufSiz>             iterator;
  typedef __deque_iterator<T, const T&, const T*, BufSiz> const_iterator;
  static size_t buffer_size() {return __deque_buf_size(BufSiz, sizeof(T)); }
#else /* __STL_NON_TYPE_TMPL_PARAM_BUG */
template <class T, class Ref, class Ptr>
struct __deque_iterator {
  typedef __deque_iterator<T, T&, T*>             iterator;
  typedef __deque_iterator<T, const T&, const T*> const_iterator;
  static size_t buffer_size() {return __deque_buf_size(0, sizeof(T)); }
#endif

  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;

  T* cur;
  T* first;
  T* last;
  map_pointer node;

  __deque_iterator(T* x, map_pointer y) 
    : cur(x), first(*y), last(*y + buffer_size()), node(y) {}
  __deque_iterator() : cur(0), first(0), last(0), node(0) {}
  __deque_iterator(const iterator& x)
    : cur(x.cur), first(x.first), last(x.last), node(x.node) {}

  reference operator*() const { return *cur; }
#ifndef __SGI_STL_NO_ARROW_OPERATOR
  pointer operator->() const { return &(operator*()); }
#endif /* __SGI_STL_NO_ARROW_OPERATOR */

  difference_type operator-(const self& x) const {
    return buffer_size() * (node - x.node - 1) +
      (cur - first) + (x.last - x.cur);
  }

  self& operator++() {
    ++cur;
    if (cur == last) {
      set_node(node + 1);
      cur = first;
    }
    return *this; 
  }
  self operator++(int)  {
    self tmp = *this;
    ++*this;
    return tmp;
  }

  self& operator--() {
    if (cur == first) {
      set_node(node - 1);
      cur = last;
    }
    --cur;
    return *this;
  }
  self operator--(int) {
    self tmp = *this;
    --*this;
    return tmp;
  }

  self& operator+=(difference_type n) {
    difference_type offset = n + (cur - first);
    if (offset >= 0 && offset < buffer_size())
      cur += n;
    else {
      difference_type node_offset =
        offset > 0 ? offset / buffer_size()
                   : -difference_type((-offset - 1) / buffer_size()) - 1;
      set_node(node + node_offset);
      cur = first + (offset - node_offset * buffer_size());
    }
    return *this;
  }

  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); }

  bool operator==(const self& x) const { return cur == x.cur; }
  bool operator!=(const self& x) const { return !(*this == x); }
  bool operator<(const self& x) const {
    return (node == x.node) ? (cur < x.cur) : (node < x.node);
  }

  void set_node(map_pointer new_node) {
    node = new_node;
    first = *new_node;
    last = first + buffer_size();
  }
};

我们一部分一部分的分析:

#ifndef __STL_NON_TYPE_TMPL_PARAM_BUG
template <class T, class Ref, class Ptr, size_t BufSiz>
struct __deque_iterator {
  typedef __deque_iterator<T, T&, T*, BufSiz>             iterator;
  typedef __deque_iterator<T, const T&, const T*, BufSiz> const_iterator;
  static size_t buffer_size() {return __deque_buf_size(BufSiz, sizeof(T)); }
#else /* __STL_NON_TYPE_TMPL_PARAM_BUG */
template <class T, class Ref, class Ptr>
struct __deque_iterator {
  typedef __deque_iterator<T, T&, T*>             iterator;
  typedef __deque_iterator<T, const T&, const T*> const_iterator;
  static size_t buffer_size() {return __deque_buf_size(0, sizeof(T)); }
#endif

首先 这一部分 根据__STL_NON_TYPE_TMPL_PARAM_BUG 这个宏来条件编译出两个不同的模板,主要区别是最后一个参数 size_t BufSize是否存在。为什么会有这个条件编译呢?
主要是因为 早期有些编译器不支持最后一个参数为int 这样子具体某个值的模板。

举个例子:
template< int i > f();
那 f< 1 >();
   f< 2 >();
  是不是由template生成的不同模板呢?不同编译器处理不同,但是现在一般都会认为他们不同

在这一段代码中,在__deque_iterator内部定义了两个字段:iterator和const_iterator。这两个迭代器都是接受三个类型参数的

typedef __deque_iterator<T, T&, T*>             iterator;
typedef __deque_iterator<T, const T&, const T*> const_iterator;  

接下来是这个__deque_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;

  T* cur;
  T* first;
  T* last;
  map_pointer node;

node是一个T类型的二重指针
cur,first,last是一个T类型的指针
(未完待续o( ̄︶ ̄)o)


(2017/09/24继续)
上午考完托福,下午继续看源码,爽歪歪 O(∩_∩)O


上面还是在讲__deque_iterator这个工具类,其中要注意的就是四个成员变量:first,cur,last和node.
__deque_iterator管理的是这么一个结构:
这里写图片描述

node就是图中的map指针,它指向一个T*数组.而这个数组的每一个元素都分别指向不同的T数组,这种T数组又被称一个Buf。first指针指向T数组的开始,last指向末尾,而cur则指向中间的一些节点。

接下来是一些构造函数:

 __deque_iterator(T* x, map_pointer y) 
    : cur(x), first(*y), last(*y + buffer_size()), node(y) {}
  __deque_iterator() : cur(0), first(0), last(0), node(0) {}
  __deque_iterator(const iterator& x)
    : cur(x.cur), first(x.first), last(x.last), node(x.node) {}

很正常的代码。

接下来 就是重载一些运算符了。
1. 重载了取值运算符*和->.他们都是返回cur的内容或cur指针内容。

  reference operator*() const { return *cur; }
#ifndef __SGI_STL_NO_ARROW_OPERATOR
  pointer operator->() const { return &(operator*()); }
#endif /* __SGI_STL_NO_ARROW_OPERATOR */

2 .设置节点:

  void set_node(map_pointer new_node) 
  {
    node = new_node;
    first = *new_node;
    last = first + buffer_size();
  }

这个函数的作用是让node去指向一个新的T*,而这个node新所指的就是一个Buf结构,其实也是一个数组。让first指向Buffer的开始,last指向Buf的最后,注意这个Buf的大小就是size_t __deque_buf_size(size_t n, size_t sz)函数的返回值,这个函数的解释在上面第二部分有提到。
3. 重载”-“:
difference_type operator-(const self& x) const {
return buffer_size() * (node - x.node - 1) +
(cur - first) + (x.last - x.cur);
}
这个是什么意思的嘞?
如下面这个图所示:
这里写图片描述
就是从*this到x之间 一共有多少个T元素的意思!
···其它一些函数 需要结合deque来看了


未完待续((^▽^))

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值