zoukankan      html  css  js  c++  java
  • C++ 《STL源码剖析》 deque 学习

    Deque 简介

        deque是“double—ended queue”的缩写,和vector一样都是STL的容器,deque 是双端数组,而 vector 是单端的。

        deque 在接口上和 vector 非常相似,在许多操作的地方可以直接替换。

        deque 可以随机存取元素(支持索引值直接存取,用[]操作符或at()方法,这个等下会详讲)。

        deque 头部和尾部添加或移除元素都非常快速。但是在中部安插元素或移除元素比较费时。

        使用时需要包含头文件 #include<deque> 。

    Deque 实现原理

      deque 的中控器

        deque是连续空间(至少逻辑上看来如此),连续线性空间总令我们联想到array或vector。array无法成长,vector虽可成长,却只能向尾端成长,而且其所谓的成长原是个假象,事实上是(1)另觅更大空间;(2)将原数据复制过去;(3)释放原空间三部曲。如果不是vector每次配置新空间时都有留下一些余裕,其成长假象所带来的代价将是相当高昂。

        deque系由一段一段的定量连续空间构成。一旦有必要在deque的前端或尾端增加新空间,便配置一段定量连续空间,串接在整个deque的头端或尾端。deque的最大任务,便是在这些分段的定量连续空间上,维护其整体连续的假象,并提供随机存取的接口。避开了“重新配置、复制、释放”的轮回,代价则是复杂的迭代器架构。

        受到分段连续线性空间的字面影响,我们可能以为deque的实现复杂度和vector相比虽不中亦不远矣,其实不然。主要因为,既是分段连续线性空间,就必须有中央控制,而为了维持整体连续的假象,数据结构的设计及迭代器前进后退等操作都颇为繁琐。deque的实现代码分量远比vector或list都多得多。

        deque采用一块所谓的map(注意,不是STL的map容器)作为主控。这里所谓map是一小块连续空间,其中每个元素(此处称为一个节点,node)都是指针,指向另一段(较大的)连续线性空间,称为缓冲区。缓冲区才是deque的储存空间主体。SGI STL 允许我们指定缓冲区大小,默认值0表示将使用512 bytes 缓冲区。

        deque的整体架构如下图所示:

        

      deque 的迭代器

        让我们思考一下,deque的迭代器应该具备什么结构,首先,它必须能够指出分段连续空间(亦即缓冲区)在哪里,其次它必须能够判断自己是否已经处于其所在缓冲区的边缘,如果是,一旦前进或后退就必须跳跃至下一个或上一个缓冲区。为了能够正确跳跃,deque必须随时掌握管控中心(map)。所以在迭代器中需要定义:当前元素的指针,当前元素所在缓冲区的起始指针,当前元素所在缓冲区的尾指针,指向map中指向所在缓区地址的指针,分别为cur, first, last, node。

        指针结构如下图所示:

        

    deque的内部结构大致就是这样

    下面简单介绍一下deque函数的删除我感觉挺有意思的

    deque<T, Alloc, BufSize>::erase(iterator first, iterator last) {
      if (first == start && last == finish) {//如果是删全部
        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;
        }
        return start + elems_before;
      }
    }

    deque对于erase这个接口

    它会根据具体未知来选择是往前移动还是往后移动

    其他的函数 类似pop push其实和之前vector和list差不多 

    先判断空间够不够 是不是指向当前缓冲区的first|last 等一系列问题

    如果空间不够 就申请新的缓冲区 然后map指针指向下一个map数组位置 然后 再对应指向新的缓冲区 顺便更新迭代器start end cur

  • 相关阅读:
    JavaScript类型转换
    JavaScript中的 typeof,null,和undefined
    JavaScript循环
    JavaScript条件语句
    JavaScript运算符
    JavaScript字符串
    JavaScript事件
    JavaScript对象,函数,作用域
    JavaScript基础
    数值数据的特征预处理
  • 原文地址:https://www.cnblogs.com/MengX/p/12336291.html
Copyright © 2011-2022 走看看