stl源码剖析序列式容器之deque

deque叫做双向队列,是一种双向开口的连续线性空间(伪连续),但是它的头部操作效率奇差无比,难以被接受,并且deque和vector的差异在于deque允许参数时间内对头端进行元素的插入和删除操作,deque没有容量的概念,动态的以分段连续空间组合而成,随时可以增加一段新的空间并且链接起来。deque也提供了random access iterator,但它的迭代器也不是普通指针,复杂度远在vector之上,因此,如果对deque实现排序,可以先将deque复制到一个vector中,将vector排序完成后,复制回deque中。deque采用一小块map(不是stl的map容器)作为主控,这个map是一小块连续空间,其中每个元素都是一个指针,指向另一端较大的连续线性空间,叫做缓冲区,缓冲区才是存储空间主体,默认值0代表使用512bytes缓冲区,map使用率如果满载,利用reallocate_map()配置一块更大的空间作为map。

    

template<class T,class Alloc=alloc,size_t BufSiz=0>
    class deque{
                public:
                    typedef T value_type;
                    typedef value_type* pointer;
                    .....
                proteced:
                    typedef pointer* map_pointer;//元素的指针的指针
                    map_pointer map;//指向map
                    size_type map_size;//map内可容纳多少指针。
    }


deque是一个分段连续空间,维持其“整体连续”假象的任务,就落在了迭代器的operator++和operator--两个运算子身上,所以deque的迭代器必须能够指出分段连续空间(缓冲区)在哪里,其次判断是否在缓冲区的边缘,并且需要跳跃至其它缓冲区,所以deque还必须掌握管控中心(map),显然它是不可能继承stl的iteration的,决定缓冲区大小的函数为buffer_size(),调用了_deque_buf_size()(全局函数),定义如下:
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中迭代器、缓冲区、中控器的相互关系。

由上图可以看出,每个缓冲区的大小的是固定的,那么当前进到边缘的时候如何进行跳跃呢?使用set_node()函数跳一个缓冲区(可能往前或者往后)。

    

void set_node(map_pointer new_node)
    {
        node=new_node;  
        first=*new_node;
        last=first+difference_type(buffer_size());//difference_type表示两个迭代器之间的距离,bufer_size表示缓冲区的大小,那么这里就相当于是返回了缓冲区的可容纳元素个数,也就是last指向的位置。所以只需要传入node+1、node-1就可以实现缓冲区指向的更改了。
    }



attention:deque的迭代器没有重载STL的Iterator

   

 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;
        // 因为没继承std::iterator,所以要自行撰写五个必要的迭代器相应型别: 
        //value_type、difference_type、reference_type、pointer_type、iterator_category.
        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;//指向指针的指针,用于确定map
        typedef __deque_iterator self;
        // 保存容器中的结点
        T* cur; // 指向当前缓冲区中的元素
        T* first; // 当前缓冲区的起点
        T* last; // 当前缓冲区的终点(包括备用空间)
        map_pointer node; // 指向管控中心
        }
        template <class T, class Ref, class Ptr, size_t BufSiz>
        inline random_access_iterator_tag
        iterator_category(const __deque_iterator<T, Ref, Ptr, BufSiz>&) {
        return random_access_iterator_tag();
        }
        template <class T, class Ref, class Ptr, size_t BufSiz>
        inline T* value_type(const __deque_iterator<T, Ref, Ptr, BufSiz>&) {
        return 0;
        }
        template <class T, class Ref, class Ptr, size_t BufSiz>
        inline ptrdiff_t* distance_type(const __deque_iterator<T, Ref, Ptr, BufSiz>&) {
        return 0;
    }



deque也重载了常用的运算子,以满足访问迭代器的要求。

    

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 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;
    }
    /**
    *    实现p+=n的功能
    *    迭代器向前移动n个元素,其中n可能为负。实现步骤如下:
    *    1、计算相对于该缓冲区起始位置的偏移量offset
    *    2、如果offset没有超出缓冲区,则直接cur+=n
    *    3、如果offset超过了缓冲区空间
    *    -- 如果offset大于0,计算向前移动多少个缓冲区,offset / difference_type(buffer_size())
    *    -- 如果offset小于0,计算向后移动多少个缓冲区,-difference_type((-offset - 1) / buffer_size()) - 1;
    *    4、调整到移动后的位置。
    */
    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;
          //如果offset是正数,就直接判断他是缓冲区大小的多少倍,往后跳n个区就可以了。
          //如果是负数,那么算它需要往前跳几个区。因为只要为负数,必定会往前跳,所以后面跟了-1。
            set_node(node + node_offset);//切换至正确的缓冲区
            cur = first + (offset - node_offset * difference_type(buffer_size()));//切换至正确的元素。
        }
        return *this;
    }
    /**
    * 实现诸如p+n的功能
    * 此函数中直接调用operator +=的函数
    */
    self operator+(difference_type n) const
    {
        self tmp = *this;
        // 这里调用了operator +=()可以自动调整指针状态
        return tmp += n;
    }
    /**
    * 实现p-=n的功能
    * 此处直接利用operator += ,改变一下n的正负即可
    */
    self& operator-=(difference_type n) { return *this += -n; }
    /**
    * 实现p-n的功能
    * 直接调用operator -=的函数
    */
    self operator-(difference_type n) const {
        self tmp = *this;
        return tmp -= n;
    }
    /**
    * 下标运算子,支持随机存取的功能,迭代器直接跳跃n个距离。n可否取未初始化,或是超出内存区域的数值,试过了,vs2010上面会报错,下标越界。
    */
    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);//先比较节点是否相等,再比较cur。
    }



deque除了维护一个指向map的指针外,它还维护着一个start和一个finish两个迭代器,分别指向第一缓冲区第一元素和最后一个缓冲区最后一个元素。deque也不停的记住目前的map的大小,因为一旦map提供的节点不足,需要重新配置一个更大的块,下面介绍下deque类当中的一些函数。

    

template<class T,class Alloc=alloc,size_t BufSiz=0>
        class deque{
                   //多个public可以合到一起,看你的习惯,有人喜欢把一类操作放到一起,也有的人喜欢所有都在一起
                  //默认为private
                    public:
                        typedef T value_type;
                        typedef value_type* pointer;
                        typedefsize_t size_type;
                    public:
                         typedef _deque_iterator<T,T&,T*,BufSiz>iterator;
                    proteced:
                        typedef pointer* map_pointer;//元素的指针的指针
                        map_pointer map;//指向map
                        size_type map_size;//map内可容纳多少指针。
                        iterator start;
                        iterator finish;
                    public://basic accessors基本访问
                        iterator begin() { return start; }    // 返回第一个节点的迭代器
                        iterator end() { return finish; }    // 返回最后一个节点的迭代器
                        const_iterator begin() const { return start; }    // const版本
                        const_iterator end() const { return finish; }    // const版本
                        /**
                        *    提供随机访问的下标运算子
                        *    这里计算实际地址的时候是经过一系列的计算得到的,效率上有缺失
                        */
                        reference operator[](size_type n) { return start[difference_type(n)]; }
                        //调用了_deque_iterator<>::operator[]
                        const_reference operator[](size_type n) const {
                        return start[difference_type(n)];
                        }
                        /**
                        * 以下函数分别返回首尾元素的引用
                        */
                        //调用了_deque_iterator<>::operator*
                        reference front() { return *start; }
                        reference back() {
                        iterator tmp = finish;
                        --tmp;//调用了_deque_iterator<>::operator--
                        return *tmp;//调用了_deque_iterator<>::operator*
                        }//为什么不用return *(finish-1);因为_deque_iterator<>没有为(finish-1)定义运算子?侯捷的原话。
                        //使用*(finish-1)的时候可以返回正确的结果。
                        const_reference front() const { return *start; }
                        const_reference back() const {
                        const_iterator tmp = finish;
                        --tmp;
                        return *tmp;
                        }
                        //    返回deque的大小,这里直接调用迭代器重载的‘-’运算符,有两个;;,虽奇怪但是合乎语法,侯捷说的。。
                        size_type size() const { return finish - start;; }
                        //    返回deque最大容量
                        //这个跟负数的二进制表示有关系,你可以看下补码。
                        //简单的说,就是size_type(-1)就是最大的size_type值(size_type就是unsigned int),
                        //这样就可以求出 max_size了,size_type代表类型,-1是值,不是很懂,依然。。。。。。
                        size_type max_size() const { return size_type(-1); }
                        // deque为空的时, 只有一个缓冲区
                        bool empty() const { return finish == start; }
        }


deque的构造和内存管理也比vector要复杂很多,deque执行定义了两个专属的空间配置器:

    

protected:
    //专属配置器,每次配置一个元素大小
    typedef simple_alloc<value_type,Alloc> data_allocator;
    //专属配置器,每次配置一个指针大小
    typedef simple_alloc<pointer,Alloc>map_allocator;
    //constructor
    deque(int n,const value_type& value):start(0),finish(),map(0),map_size(0)
    {fill_initialize(n,value);//产生以及安排deque结构,并且设置好元素的初值。}
    template<calss T,class Alloc,size_t BufSize>
    void deque<T,Alloc,BufSize>::fill_initialize(size_type n,const value_type& value)
    {
        create_map_and_nodes(n);//把deque的结构都产生并安排好
        map_pointer cur;
        _STL_TRY{
            //为每个节点的缓冲区设定初值
        for(cur=start.node;cur<finish.node;++cur)
             uninitialized_fill(*cur,*cur+buffer_size(),value);
             uninitialized_fill(finish.first,finish.cur,value);
             //最后一个节点的设置和上面的几个节点不一样,因为尾端可能会存在备用空间,备用空间不用设定初值。
        
        }
        catch(.....)
        {....}
    }
    void deque<T,Alloc,BufSize>::create_map_and_nodes(size_type num_elements)
    {
        //需要节点数=(元素个数/每个缓冲区可容纳元素个数)+1,不然不够,余数没有缓冲区放
        //刚好整除也会多分配一个节点
        size_type num_nodes=num_elements/buffer_size()+1;
        //一个map要管理几个节点,最少管理8个,最多是所需节点数+2,前后预留一个,扩充时候使用
        map_size=max(initial_map_size(),num_nodes+2);
        map=map_allocator::allocate(map_size);//配置一个具有map_size个节点的map,map指向配置空间起始位置。
        //以下令nstart和nfinish指向map所拥有的全部节点最中央区段
        //保持在中央,可使头尾两端扩充区间一样,每个节点可对应一个缓冲区
        map_pointer nstart=map+(map_size-num_nodes)/2;
        map_pointer nfinish=nstart+num_nodes-1;
        map_pointer cur;
        _STL_TRY{
        //为map内每个现用节点配置缓冲区,所有的缓冲区加起来就是deque的可用空间。
        for(cur=nstart;cur<+nfinish;++cur)
            *cur=allocate_node();//将节点和缓冲区对应起来。
        }
        catch(...)
        {//"commit or rollback"全部成功,或者一个不留
            .......
        }
        //设置start和end
        start.set_node(nstart);
        finish.set_node(nfinish);//设置他们指向第几个位置
        start.cur=start.first;//设置他们指向哪个缓冲区
        finish.cur=finish.first+num_elements%buffer_size();
        //前面说过,如果刚好整除,会多分配一个节点,此时可以让cur指向多分配的这个缓冲区的起始处。
    }
    public:
    void push_back(const value_type& t)
    {
        if(finish.cur!=finish.last-1){
           //两个或以上的备用空间
           construct(finish.cur,t);//直接在备用空间上构造元素
           ++finish.cur;//调整最后缓冲区的使用状态
    }
           else//缓冲区只有一个元素备用空间
                push_back_aux(t);//分配新的缓冲区
    }
    void deque< T, Alloc, BufSize>::push_back_aux(const value_type& t)
    {
        value_type t_copy=t;
        reserve_map_at_back();//是否需要更换map
        *(finish.node+1)=allocate_node();//配置一个新节点(缓冲区)
        _STL_TRY{
            construct(finish.cur,t_copy);
            finish.set_node(finish.node+1);
            finish.cur=finish.first;
        }
        _STL_UNWIND(deallocate_node(*(finish.node+1));//配置不成功
    }
    void push_front(const value_type& t)
    {
        if(start.cur!=start.first){//第一缓冲区尚有备用空间
           //两个或以上的备用空间
           construct(start.cur-1,t);//直接在备用空间上构造元素
           --start.cur;//调整最后缓冲区的使用状态
    }
           else//第一缓冲区无元素备用空间,因为front指针指向的是下一个可用空间,而start指向的是第一个使用空间,所以不一样。
                push_front_aux(t);//分配新的缓冲区
    }
    void deque< T, Alloc, BufSize>::push_front_aux(const value_type& t)
    {
        value_type t_copy=t;
        reserve_map_at_front();//是否需要更换map
        *(start.node-1)=allocate_node();//配置一个新节点(缓冲区)
        _STL_TRY{
            start.set_node(start.node-1);
            //明显可见,指向倒数第一个元素空间
            start.cur=start.last-1;
            construct(start.cur,t_copy);
        }
        catch(...){
            //"commit or rollback"
            start.set_node(start.node+1);
            start.cur=start.first;
            deallocate_node(*(start.node-1));
            throw;
        }
        
    }
    void reserve_map_at_back (size_type nodes_to_add = 1)
    {
        if (nodes_to_add + 1 > map_size - (finish.node - map))//map的尾端备用节点空间不足了。
            // 此时,需要调整map,更换一个更大的map(配置更大的,拷贝原来的,释放原来的)
            reallocate_map(nodes_to_add, false);
    }
    void reserve_map_at_front (size_type nodes_to_add = 1)
    {
        if (nodes_to_add  > start.node-map)//map的前端备用节点空间不足了。
            // 此时,需要调整map,更换一个更大的map(配置更大的,拷贝原来的,释放原来的)
            reallocate_map(nodes_to_add, true);
    }
    /**
     * 重新配置map, 不会对缓冲区进行操作, map维护的是指向缓冲区的指针
     */
    template <class T, class Alloc, size_t BufSize>
    void deque<T, Alloc, BufSize>::reallocate_map(size_type nodes_to_add,
        bool add_at_front)
    {
      size_type old_num_nodes = finish.node - start.node + 1;
      size_type new_num_nodes = old_num_nodes + nodes_to_add;
    
      map_pointer new_nstart;
      // 此处为了防止出现一端已经用完,另一端却还有很多剩余的情况
      if (map_size > 2 * new_num_nodes) {
        // 调整新的map中的起始点
        new_nstart = map + (map_size - new_num_nodes)/2 + (add_at_front ? nodes_to_add : 0);
        // 如果前端剩余很多
        if (new_nstart < start.node)
          copy(start.node, finish.node + 1, new_nstart);//如果是往前端移动,那么从头至尾开始复制。
        else   // 尾端剩余很多,那就是往尾端移动,从尾到头复制。
          copy_backward(start.node, finish.node + 1, new_nstart + old_num_nodes);
      }
      else {    // map不够用了,就需要配置一块更大的map
        size_type new_map_size = map_size + max(map_size, nodes_to_add) + 2;
        // 配置一块大的map
        map_pointer new_map = map_allocator::allocate(new_map_size);
        // attention:始终要使start和finish处在map空间的中间,因为后续要拷贝,所以new_start的起始位置就要考虑到后面是在前面插入还是后面插入了,假如插入在前面,new_start会往后移动一点,那么往前插入之后就基本又是在中间了。
        new_nstart = new_map + (new_map_size - new_num_nodes)/2 + (add_at_front ? nodes_to_add : 0);
        // 拷贝到新的map空间中去
        copy(start.node, finish.node + 1, new_nstart);
        // 释放旧的空间
        map_allocator::deallocate(map, map_size);
        // 改变map和size参数
        map = new_map;
        map_size = new_map_size;
      }
      // 调整新的start和finish
      start.set_node(new_nstart);
      finish.set_node(new_nstart + old_num_nodes - 1);//因为这里只是分配空间,并不插入节点
    }


上述是针对deque的结构和内存管理的操作,接下来讲一讲元素操作,类似于其它容器,它也有pop_back、pop_front、clear、erase、insert等等。对了,在deque的迭代器中,*itr其实和*(itr.cur)的输出结果是一样的,可以从前面我们的重载代码看出,*运算符输出的是迭代器的.cur。所以对于下面这个itr=find(deque.begin(),deque.end(),value),在找到的情况下,输出*(itr)或者输出*(tr.cur)结果是一样的,都是value(itr是迭代器,deque<int>::iterator itr)。
    
    

void pop_back()
    {
        if(finish.cur!=finish.first)
    {
        //最后一个缓冲区有元素。
       --finish.cur;//调整指针,相当于排除了最后的元素。
       destroy(finish.cur)//析构元素,释放空间。
    }
    else
        //最后一个缓冲区没有任何元素
        pop_back_aux();//这里进行缓冲区的释放工作,调整map
    }
    /**
     * 在pop_back中,如果碰到为首元素的情况,调用此函数,就是缓冲区内无元素。
     */
    template <class T, class Alloc, size_t BufSize>
    void deque<T, Alloc, BufSize>:: pop_back_aux()
    {
      deallocate_node(finish.first);    // 释放节点
      finish.set_node(finish.node - 1); // 重新设定finish,使之指向上一个缓冲区的最后一个元素
      finish.cur = finish.last - 1;
      destroy(finish.cur);  //将该元素析构掉
    }
    void pop_front() {
      if (start.cur != start.last - 1)
      {
        //第一个缓冲区有两个或以上的元素。
        destroy(start.cur);//析构掉这个元素
        ++start.cur;//调整指针,相当于排除了第一个元素。
      }
      else
        pop_front_aux();//第一个缓冲区仅仅一个元素,那么就直接将这个缓冲区释放掉。
    }
    /**
     * 只有在start.cur == start.last - 1的时候调用
     * 此时需要调整map
     */
    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 + 1);//调整start位置,使它指向下一个缓冲区的第一个元素
      start.cur = start.first;
    }
    //clear是用来清除整个deque的,但是无元素时初始状态也会保有一个缓冲区。所以clear回到初始状态,也会保留一个缓冲区。
    void deque<T, Alloc, BufSize>::clear()
    {
             // 首先析构头尾以外的缓冲区的所有元素, 并释放相应空间,因为这些缓冲区一定是满的。
          for (map_pointer node = start.node + 1; node < finish.node; ++node) {
            //将缓冲区内的元素都析构掉,调用的是destory的第二个版本。
            destroy(*node, *node + buffer_size());
            //释放内存。
            data_allocator::deallocate(*node, buffer_size());
          }
        
          // 如果deque至少有头尾两个缓冲区,析构所有对象, 并释放掉结尾的内存,保留头缓冲区。
          if (start.node != finish.node) {
            destroy(start.cur, start.last);  // 将头缓冲区的元素析构
            destroy(finish.first, finish.cur);  //将尾缓冲区的元素析构
            data_allocator::deallocate(finish.first, buffer_size()); // 头缓冲区保留,释放尾缓冲区
          }
          // 析构所有元素, 但是不释放空间, 因为deque要满足这个前置条件
          else//只有一个缓冲区,必须保留唯一的缓冲区。
            destroy(start.cur, finish.cur);
        
          finish = start; // 调整finish
    }
    /**
     * 此函数实现擦除单个指定元素
     */
    iterator erase(iterator pos)//清除pos所指的元素,pos为清楚点。
    {
      iterator next = pos;
      ++next;
      // 计算待擦除点前的元素个数
      difference_type index = pos - start;
      // 判断待擦除结点前后元素的个数, 哪部分少就移动哪部分
      if (index < (size() >> 1))//看index是否小于长度的一半,小于则说明前方的元素比较少。。智障了,尽然想了一会。
      {
        // 前面部分的元素少
        copy_backward(start, pos, next);  
        pop_front();//移动完毕,最前一个元素冗余,去除掉。
      }
      // 后面部分的元素少
      else {
        copy(next, finish, pos); 
        pop_back();//移动完毕,最后一个元素冗余,去除掉。
      }
      return start + index;
    }
    //擦除[first,last)区间的元素。此函数按下列步骤来擦除区间.1.需要擦除整个空间,直接调用clear()2.需要擦出中间指定区间
    //3.擦除中间指定区间,需要考虑一下两种情况:(1)区间前面的元素少,就移动前面的元素(2)区间后面的元素少,就移动后面的元素
    template <class T, class Alloc, size_t BufSize>
    deque<T, Alloc, BufSize>::iterator
    deque<T, Alloc, BufSize>::erase(iterator first, iterator last)
    {
      if (first == start && last == finish) {   // 需要擦除整个deque,清除区间就是整个deque
        clear();
        return finish;
      }
      else {
        difference_type n = last - first;   // 清除区间的长度
        difference_type elems_before = first - start;  // 待清除区间前方的元素个数
        if (elems_before < (size() - n) / 2) {  // 如果前方的元素个数较少
          copy_backward(start, first, last);    // 向后移动前方元素,覆盖掉清除区间
          iterator new_start = start + n;     // 调整新的起始点
          destroy(start, new_start);          // 全局函数,析构冗余节点元素,
          for (map_pointer cur = start.node; cur < new_start.node; ++cur)
            data_allocator::deallocate(*cur, buffer_size());   // 释放冗余的缓冲区空间
          start = new_start;
        }
        else {    // 后方元素比较少的情况
          copy(last, finish, first);    // 向前移动后方元素,同样是覆盖掉清除区间
          iterator new_finish = finish - n; // 标记新的尾点
          destroy(new_finish, finish);      // 全局函数,析构节点元素
          for (map_pointer cur = new_finish.node + 1; cur <= finish.node; ++cur)
            data_allocator::deallocate(*cur, buffer_size());  // 释放缓冲区空间
          finish = new_finish;//设置deque的新尾点。
        }
        return start + elems_before;
      }
    }
    //插入功能的函数deque提供了许多个版本,最基础最重要的就是下面这个版本,允许在某个点之前插入一个元素,并设定其值。
    //在position出插入值为x的元素。
    iterator insert(iterator position, const value_type& x)
    {
      // 如果是在deque的最前端插入, 那么直接push_front()即可
      if (position.cur == start.cur) {
        push_front(x);
        return start;
      }
      // 如果是在deque的末尾插入, 直接调用push_back()
      else if (position.cur == finish.cur) {
        push_back(x);
        iterator tmp = finish;
        --tmp;
        return tmp;//要返回指向插入位置前方的迭代器。
      }
      else {
        return insert_aux(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_type x_copy = x;
    
      if (index < size() / 2) {  // 如果前端的元素比较少
        push_front(front());  // 在最前面插入一个与第一个元素一样的数
        iterator front1 = start;  // 记录起始点
        ++front1;
        iterator front2 = front1; 
        ++front2;
        pos = start + index;
        iterator pos1 = pos;
        ++pos1;//copy是半开半闭的嘛,并且是前向插入,pos1就指向插入元素应该出现位置的下一个位置。
        copy(front2, pos1, front1); // 拷贝空间,将[front2,pos1)拷贝到front1以后,其中front2覆盖掉front1.
      }
      else {   // 后端的元素比较少,原理用上
        push_back(back());
        iterator back1 = finish;
        --back1;
        iterator back2 = back1;
        --back2;
        pos = start + index;
        copy_backward(pos, back2, back1);//元素后向复制
      }
      *pos = x_copy;//插入位置赋上新值。
      return pos;
    }
    //下面是插入n个元素的插入函数。
    template <class T, class Alloc, size_t BufSize>
    void deque<T, Alloc, BufSize>::insert(iterator pos,
                                          size_type n, const value_type& x)
    {
      if (pos.cur == start.cur) {   // 如果插入点再最前端
        iterator new_start = reserve_elements_at_front(n); // 插入区间前方备用空间能容纳n个元素,调整新的start位置
        uninitialized_fill(new_start, start, x);    //直接在前端构造n个元素
        start = new_start;  // 调整新的start
      }
      else if (pos.cur == finish.cur) {
        // 与reserve_elements_at_front相同
        iterator new_finish = reserve_elements_at_back(n); 
        uninitialized_fill(finish, new_finish, x);
        finish = new_finish;
      }
      else
        insert_aux(pos, n, x);
    }
    /**
     * 插入区间前方备用空间能否容纳n个元素
     */
    iterator reserve_elements_at_front(size_type n)
    {
      size_type vacancies = start.cur - start.first;
      if (n > vacancies)   // 如果容纳不了,就需要重新配置map
        new_elements_at_front(n - vacancies);
      return start - difference_type(n);
    }
    /**
     * 只有在前方备用空间容纳不了待插入的n个元素的情况下调用
     */
    template <class T, class Alloc, size_t BufSize>
    void deque<T, Alloc, BufSize>::new_elements_at_front(size_type new_elements)
    {
      size_type new_nodes = (new_elements + buffer_size() - 1) / buffer_size();
      reserve_map_at_front(new_nodes);  // 调整map
      size_type i;
      __STL_TRY {
        for (i = 1; i <= new_nodes; ++i)
          *(start.node - i) = allocate_node(); // 为每一个map指针配置空间
      }
      catch (...) {
        for (size_type j = 1; j < i; ++j)
          deallocate_node(*(start.node - j));
        throw;
      }
    }
    /**
     * 调整map的前端,以在前端能连接更多缓冲区
     */
    void reserve_map_at_front (size_type nodes_to_add = 1)
    {
      if (nodes_to_add > start.node - map)
        reallocate_map(nodes_to_add, true);  // 此函数上面有说明
    }
    
    /**
     * 好吧,这里才是最重要的insert_aux函数,实现在中间某个位置插入n个元素
     */
    template <class T, class Alloc, size_t BufSize>
    void deque<T, Alloc, BufSize>::insert_aux(iterator pos,
        size_type n, const value_type& x)
    {
      const difference_type elems_before = pos - start;  // 计算该位置前面的元素个数
      size_type length = size();
      value_type x_copy = x;
      if (elems_before < length / 2) {  // 如果位置前面的元素比较少
        iterator new_start = reserve_elements_at_front(n); // 同上
        iterator old_start = start;
        pos = start + elems_before;
        __STL_TRY {
          if (elems_before >= difference_type(n)) { 
            iterator start_n = start + difference_type(n);
            uninitialized_copy(start, start_n, new_start);
            start = new_start;
            copy(start_n, pos, old_start);
            fill(pos - difference_type(n), pos, x_copy);
          }
          else {
            __uninitialized_copy_fill(start, pos, new_start, start, x_copy);
            start = new_start;
            fill(old_start, pos, x_copy);
          }
        }
        __STL_UNWIND(destroy_nodes_at_front(new_start));
      }
      else {   // 该位置后面的元素比较少
        iterator new_finish = reserve_elements_at_back(n);
        iterator old_finish = finish;
        const difference_type elems_after = difference_type(length) - elems_before;
        pos = finish - elems_after;
        __STL_TRY {
          if (elems_after > difference_type(n)) {
            iterator finish_n = finish - difference_type(n);
            uninitialized_copy(finish_n, finish, finish);
            finish = new_finish;
            copy_backward(pos, finish_n, old_finish);
            fill(pos, pos + difference_type(n), x_copy);
          }
          else {
            __uninitialized_fill_copy(finish, pos + difference_type(n),
            x_copy,
            pos, finish);
            finish = new_finish;
            fill(pos, old_finish, x_copy);
          }
        }
        __STL_UNWIND(destroy_nodes_at_back(new_finish));
      }
    }



不得不说,deque是真的复杂,但是stack和queue是以它为底部结构实现的(缺省情况),所以接下来的介绍就很轻松了,还有就是deque为了实现整体连续的假象,导致其实现起来比较繁琐,尽量少使用它。另外,重复一遍,如果需要对deque进行排序的话,最好是先复制到vector中,然后再进行排序,最后在把元素拷贝回来,这样效率会提高一点。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值