SGISTL源码阅读十四 deque容器上

SGISTL源码阅读十四 deque容器上

deque概述

之前我们学习过vector,我们知道他的扩容方式是从尾部扩容,也就是它是单向开口的。dequevectorlist都要更复杂一些,它是双向开口的,也就是说可以在头部和尾部做同样的操作(我们要向vector的头部添加一个元素代价是相当大的,需要将所有元素向后移动,但是对于deque来说,在头部和尾部操作都是同样的时间复杂度)。这样看起来deque是一段连续线性空间,但是它只是逻辑上的一整段连续线性空间,内部实现是由一小段一小段的定量连续空间构成的。下面我们来深入了解它。

深入源码

deque的数据结构
map(deque的中控器)

这里的map和容器map不一样,它是deque的中控器。前面提到deque是由一小段一小段的定量连续空间构成的,将这些小段的定量连续空间整合在一起,就是它的作用了。它用到了一个二级指针。

//缺省使用SGISTL的空间配置器
template <class T, class Alloc = alloc, size_t BufSiz = 0>
class deque {
public:
  typedef T value_type;
  //当前类型的指针 别名为pointer
  typedef value_type* pointer;
  typedef const value_type* const_pointer;
  //...
protected:
  //二级指针
  typedef pointer* map_pointer;
  typedef simple_alloc<pointer, Alloc> map_allocator;
  //...
  //map初始长度为8
  static size_type initial_map_size() { return 8; }
  //...
protected:
  //它的内部维护了两个迭代器,分别指向了deque的第一个节点和最后一个节点
  iterator start;
  iterator finish;

  map_pointer map;
  size_type map_size;	//map内的指针个数

一些基本的操作

public:
  //返回第一个节点
  iterator begin() { return start; }
  //返回末尾节点
  iterator end() { return finish; }
  const_iterator begin() const { return start; }
  const_iterator end() const { return finish; }
  
  //重载操作符(调用__deque_iterator的重载操作符[]),实现下标访问
  reference operator[](size_type n) { return start[difference_type(n)]; }
  const_reference operator[](size_type n) const {
    return start[difference_type(n)];
  }
  //返回第一个节点的值
  reference front() { return *start; }
  //返回末尾值,由于[start,finish)
  reference back() {
    iterator tmp = finish;
    --tmp;
    return *tmp;
  }
  const_reference front() const { return *start; }
  const_reference back() const {
    const_iterator tmp = finish;
    --tmp;
    return *tmp;
  }

我们通过图片来加深对deque的理解
宏观
在这里插入图片描述
微观
在这里插入图片描述

deque的迭代器

deque的迭代器类型是random_access_iterator_tag,但是它并不是简单的普通指针。

//全局函数
//如果n不为0,则直接返回n
//如果n为0,sz < 512bytes,则返回512 / sz
//如果n为0,sz > 512bytes,则返回size_t(1)
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));
}
//...
public:
#ifndef __STL_NON_TYPE_TMPL_PARAM_BUG
  typedef __deque_iterator<T, T&, T*, BufSiz>              iterator;
  typedef __deque_iterator<T, const T&, const T&, BufSiz>  const_iterator;
#else /* __STL_NON_TYPE_TMPL_PARAM_BUG */
  typedef __deque_iterator<T, T&, T*>                      iterator;
  typedef __deque_iterator<T, const T&, const T*>          const_iterator;
#endif /* __STL_NON_TYPE_TMPL_PARAM_BUG */

//deque迭代器
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;
  //调用了全局函数__deque_buf_size
  static size_t buffer_size() {return __deque_buf_size(BufSiz, sizeof(T)); }
  //没有继承std::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;
  //维护了3个指针
  T* cur;	//当前迭代器所指缓冲区中的当前(current)元素
  T* first;  //当前迭代器所指缓冲区的头
  T* last;  //当前得带其所指缓冲区的尾
  map_pointer node; //指向中控器
  
  //deque迭代器的构造函数
  __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) {}

set_node()
这个函数相当的重要。我们能调用它跳出当前线性空间(缓冲区)

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

操作符重载

  //重载*,返回cur指向元素的值
  reference operator*() const { return *cur; }
#ifndef __SGI_STL_NO_ARROW_OPERATOR
  //重载->,返回cur指向元素的值
  pointer operator->() const { return &(operator*()); }
#endif /* __SGI_STL_NO_ARROW_OPERATOR */
  /* 重载操作符-
   * 计算两个迭代器指向的元素之间的个数
   * (node - x.node - 1)计算的是两个迭代器所在的线性空间中间跨过的完整的线性空间个数
   */
  difference_type operator-(const self& x) const {
    return difference_type(buffer_size()) * (node - x.node - 1) +
      (cur - first) + (x.last - x.cur);
  }
  //重载后++
  self& operator++() {
    ++cur;
    如果+1后指向last,说明需要到下一段线性空间(缓冲区)中去取值
    if (cur == last) {
      //调用set_node函数找到下一段线性空间
      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;
  }
  //重载+=
  //如果+n后的值不在当前线性空间(缓冲区),仍然需要set_node到相应线性空间(缓冲区)去
  self& operator+=(difference_type n) {
    difference_type offset = n + (cur - first);
    if (offset >= 0 && offset < difference_type(buffer_size()))
      cur += n;
    else {
      difference_type node_offset =
        offset > 0 ? offset / difference_type(buffer_size())
                   : -difference_type((-offset - 1) / buffer_size()) - 1;
      set_node(node + node_offset);
      cur = first + (offset - node_offset * difference_type(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);
  }

在学习了deque的迭代器之后,我们再用一个图来深入理解deque的结构体以及它的迭代器
在这里插入图片描述

总结

我们主要介绍了deque的数据结构以及它的迭代器。它确实是一种相对来说比较复杂的容器,但是仔细阅读源码并结合图示也并不难理解。
因为我们本节并没有介绍到关于节点node的概念,其实他由就是map所维护的。
之后我们将继续介绍deque的构造以及内存管理等。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值