• 深入剖析deque容器实现


    deque简介

           deque是双向开口的连续性存储空间。虽说是连续性存储空间,但这种连续性只是表面上的,实际上它的内存是动态分配的,它在堆上分配了一块一块的动态储存区,每一块动态存储区本身是连续的,deque自身的机制把这一块一块的存储区虚拟地连在一起。

           它首次插入一个元素,默认会动态分配512字节空间,当这512字节空间用完后,它会再动态分配自己另外的512字节空间,然后虚拟地连在一起。deque的这种设计使得它具有比vector复杂得多的架构、算法和迭代器设计。它的性能损失比之vector,是几个数量级的差别。所以说,deque要慎用。

    deque数据结构

            逻辑数据结构示意图:

                     

                                                                  图1 逻辑结构示意图

            实际数据结构示意图:

                    

                                                                     图2 实际结构示意图

     数据结构实现

    template<class _Ty, class _Ax>
    class deque
    {
    private:
    	_Mapptr _Map;		// pointer to array of pointers to blocks
    	size_type _Mapsize;	// size of map array
    	size_type _Myoff;	// offset of initial element
    	size_type _Mysize;	// current length of sequence
    };
     一、deque的中控器      
            我们发现这里有一个_Map成员变量。大家注意,这可不是STL中的map。这个_Map是一个指针,指向一块特殊的内存地址,这里保存着指向deque动态申请的所有512字节内存空间的首地址。deque先用一段小的连续空间顺序存放了一个一个指针,然后这些顺序存放的指针再各自指向用来真正存放数据的512字节连续性空间。当_Map指向的这块空间不够存放内存指针的时候,就会另觅一块更大的连续性空间,然后把指针一个一个复制过去,并销毁旧的空间。利用这种数据结构,deque就能方便地模拟自身的存储区是连续性空间的假象,并且可以实现双向插入删除的功能我们看一下deque的数据结构图,如图3所示。
                   
                                                                          图 3 内部实现结构    
             _Map所指的一小块连续空间就是deque的中央控制器,其中每个元素(此处称为一个节点,node)都是指针,指向另一段(较大的)连续线性空间,称为缓冲区。缓冲区才是deque的储存空间主体。STL 允许我们指定缓冲区大小,默认值0表示将使用512 bytes 缓冲区。
              当中控器使用率已经满载时,便需要再找一块更大的空间来做map,配置策略间reallocate_map(),新的map可以容纳更多的节点,也就是更多的缓冲区,如图4是扩展新空间的示意图,新增缓冲区是在原来基础上进行,没有复制操作,这种扩展方式避开了“重新配置、复制、释放”的轮回,代价是复杂的迭代器架
           
                                                       图 4 扩展新空间实现方式 
         从deque扩展新空间的方式,可以看出deque没有capacity(),不需要reserce(size_type n)。这是因为deque由动态分配的连续空间组合而成,随时可以增加一段新的空间链接起来。它没有必要像vector那样“因旧空间不足而重新分配2倍的空间,然后复制元素,再释放旧空间。
     二、deque的迭代器
               在STL中,我们都可以通过解引用的方式得到迭代器所指的值,为了能够从“deque内部结构”中得到中某个元素的确切位置,那么deque的迭代器应该具备怎么结构,我们可以考虑以下几点。首先,需要知道该元素位于哪个缓冲区的哪个位置;其次,一旦迭代器前进和后退有可能会跳跃至上一个或下一个缓冲区,为了判断跳跃的条件就需要知道,当前元素所在缓冲区的首尾指针。最后,如果前进或后退必须跳跃至下一个或上一个缓冲区。为了能够正确跳跃,deque必须随时掌握管控中心(map),通过map可以知道跳跃的缓冲区。所以在迭代器中需要定义如下参数,deque的迭代器如图5所示:
    • 当前元素的指针
    • 当前元素所在缓冲区的起始指针,
    • 当前元素所在缓冲区的尾指针,
    • 指向map中指向所在缓区地址的指针。
      
                                                        图 5 迭代器示意图

    deque对用户屏蔽了复杂的内存管理操作, 我们平时操时都是按照"逻辑结构"方式进行,也就是可动态插入、删除的一维数组,大大简化了用户使用,如图6所示。其实deque最大的任务就是管理这些分段的连续空间上,维护其整体连续的假象,并提供随机存取的接口。

    图 6 deque<string>

  • 相关阅读:
    C#入门
    使用 OLEDB 及 SqlBulkCopy 将多个不在同一文件夹下的 ACCESS mdb 数据文件导入MSSQL
    aspose.word 读取word段落内容
    Jquery+Aajax 批量上传
    asp.net mvc web api Token验证
    iframe父页面和子页面获取元素和js变量
    JavaScrpt常用的封装方法
    ASP.NET MVC 导出Word报表
    Asp.net的对Excel文档的导入导出操作
    C++ 复制vector值到array,复制 array 到jintArray
  • 原文地址:https://www.cnblogs.com/jinxiang1224/p/8468417.html
Copyright © 2020-2023  润新知