STL源码剖析——deque的实现

本文详细剖析了STL中的deque容器,从deque的基本概念、内部数据结构到主要操作,如push_front和push_back的实现逻辑,以及迭代器的设计,揭示了deque作为双向开口容器的高效性和灵活性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

deque简介

deque是一个双向开口的容器,可在头尾两端插入和删除元素,deque由动态的连续空间组合而成,因为迭代器的良好设计,提供了随机访问,造成一种deque为连续空间的假象

deque的数据结构

deque有一个二级指针map,map指向一小块空间,其中的每个元素都是指针,指向一段连续线性空间,称为缓冲区,缓冲区为deque存储的主体,其中存放元素

tempalte <class T, class Alloc = alloc>
class deque {
public:
    typedef T   value_type;
    typedef T*  pointer;
    typedef T** map_pointer;
protected:
    iterator  start;
    iterator  finish;
    map_pointer map;
    size_type size;         //map内指针个数
    ......
}

iterator 结构如下:

template <class T>
class deque_iterator {
public:
    typedef T**    map_pointer;
    T*  cur;            //指向所指缓冲区当前元素
    T*  first;          //首元素
    T*  last;           //尾元素后一个
    map_pointer node;   //指向控制中心
}    

用图表示如下
这里写图片描述

deque的主要操作

1.push_front

这里写图片描述

当start指向的缓冲区无备用空间时,执行push_front_aux
push_front_aux操作如下:
判断map前端的备用节点是否充足,如果充足,则再分配一个缓冲区,start.node - 1指向它,然后改节点,构造元素即可
如果map前端备用节点不足,如果map_size够大,则并不需要重新分配map, 只需要移动map中的元素即可,最后重置start, finish迭代器即可
如果map_size不够大,则要重新分配map

2.push_back

操作与push_front类似

3.pop_front

void pop_front() {
    if (start.cur != start.last - 1) {
        //第一缓冲区有多个元素
        destroy(start.cur);
        ++start.cur;
    }
    else
        pop_front_aux();
}

void pop_front_aux() {
    destroy(start.cur);
    deallocate_node(start.first);      //回收缓冲区空间
    start.set_node(start.node + 1);    //重置迭代器
    start.cur = start.first;
}

4.pop_back

void pop_back() {
    if (finish.cur != finish.first) {
        //最后缓冲区有元素
        --finish.cur;
        destroy(finish.cur);
    }
    else
        pop_back_aux();
}

void pop_back_aux() {
    deallocate_node(finish.first);    //回收缓冲区空间
    finish.set_node(finish.node - 1); //重置迭代器
    finish.cur = finish.last - 1;
    destroy(finish.cur);
}

deque迭代器完整实现

template <class T>
class deque_iterator {
public:
    typedef deque_iterator<T>           iterator;
    typedef random_access_iterator_tag  iterator_category;
    typedef T                           value_type;
    typedef T*                          pointer;
    typedef T&                          reference;
    typedef ptrdiff_t                   difference_type;
    typedef T**                         map_pointer;
    typedef deque_iterator              self;

    T*  cur;            //指向所指缓冲区当前元素
    T*  first;          //首元素
    T*  last;           //尾元素后一个
    map_pointer node;   //指向控制中心

    deque_iterator():cur(0), first(0), last(0), node(0){}
    deque_iterator(const deque_iterator& other):
        cur(other.cur), first(other.first), last(other.last), node(other.node){}

  static size_t buffer_size() {
       return deque_buffer_size(sizeof(T));
   }

   //重新设置指向管控中心的指针,修改first, last指向缓冲区
   void set_node(map_pointer new_node) {
       node = new_node;
       first = *new_node;
       last = *new_node + (difference_type)buffer_size();
   }

   reference operator* () const {
       return *cur;
   }

   pointer operator-> () const {
       return &(operator*());
   }

   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;
       if (cur == last) {
           set_node(node + 1);
           cur = first;
       }
       return *this;
   }
   //后置++
   self operator++ (int) {
       self temp = *this;
       ++*this;
       return temp;
   }
   //前置--
   self& operator-- () {
       if (cur == first) {
           set_node(node - 1);
           cur = last;
       }
       --cur;
       return *this;
   }
   //后置--
   self operator-- (int) {
       self temp = *this;
       --*this;
       return temp;
   }

   //实现随机存取
   self& operator+= (difference_type n) {
       difference_type offset = n + cur - first;
       size_t size = buffer_size();
       if (offset >= 0 && offset < size)
           cur += n;
       else {
           //不在同一缓冲区内
           difference_type node_offset = offset > 0 ?
               offset / size :
               -difference_type((-offset - 1) / size) - 1;
           //重置node,设置cur指向
           set_node(node + node_offset);
           cur = first + (offset - node_offset * size);//offset正负都适用
       }
       return *this;
   }

   self operator+ (difference_type n) const {
       self temp = *this;
       return temp += n;
   }

   self& operator-= (difference_type n) {
       return *this += -n;
   }

   self operator- (difference_type n) const {
       self temp = *this;
       return temp -= n;
   }

   //重载[],随机存取
   reference operator[] (difference_type n) const {
       return *(*this + n);
   }

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

为了将deque的迭代器使用起来看似是普通指针需要费些功夫

小结:

由deque的构造我们可以很自然的想到,用deque作为底层容器来实现stack,queue,对于stack,queue的所有操作只需要对deque转调用即可

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值