• STL略观——deque的中控器和迭代器


      vector 是单向开口的连续线性空间,deque 则是一种双向开口的连续线性空间,也就是说deque能够在其容器两端分别做元素的插入和删除操作,vector 

    当然也可以从头尾两端进行操作,不过其头部插入或者删除元素的效率贼差,建议大家少用为妙。

      

      deque 是连续空间,但是连续却是假象,实际上 deque 是由一段一段的定量连续构成,一旦需要在 deque 的头部和尾部增加存储空间,便配置一段连续的内存空间,串接在整个 deque 的头部或者尾部。deque 的最大任务便是在这些分段的定量连续空间上,维护其整体连续的假象,并提供随机访问存取的接口,避开重新配置、复制、释放的轮回,代价就是复杂的迭代器架构。deque 采用映射(map)作为主控,也就是中控器。就是一段连续的内存空间存放一大堆指针,每个指针都指向一段定量连续空间,也叫作缓冲区,缓冲区才是 deque 的主体。当我们申请对象开辟内存的时候,如果没指定缓冲区大小,默认为512bytes,注意开辟内存存的是指针,缓冲区才是存数据。


    template <class _Tp, class _Alloc = __STL_DEFAULT_ALLOCATOR(_Tp) >
    class deque : protected _Deque_base<_Tp, _Alloc> 
    {
    
        typedef _Deque_base<_Tp, _Alloc> _Base;
    public:                         // Basic types
        typedef _Tp value_type;     
        typedef value_type* pointer;
    
    protected:                      // Internal typedefs
        typedef pointer* _Map_pointer;  //元素的指针的指针
        static size_t _S_buffer_size() { return __deque_buf_size(sizeof(_Tp)); }
    
    protected:
    #ifdef __STL_USE_NAMESPACES
        using _Base::_M_map;       //指向map,map是块连续空间,其中的每个元素都是一个指针(节点),指向一块缓冲区
        using _Base::_M_map_size;  //map内可容纳多个指针
        using _Base::_M_start;
        using _Base::_M_finish;
    #endif /* __STL_USE_NAMESPACES */
    }

      deuqe 是分段连续空间,为了保证其连续的假象,迭代器运算子就必须好好设计,也就是 operator++ 和 operator-- 两个运算子。deque 迭代器得明白自己的任务,首先,必须能够指出缓冲区在哪里,其次它必须能够判断自己是否已经处于其所在缓冲区的边缘,一旦是,前进或者后退时就必须跳跃至下一个或者上一个缓冲区,所以 deque 应该随时掌握中控器(map)。以下是其迭代器的源代码:

    template <class _Tp, class _Ref, class _Ptr>
    struct _Deque_iterator             
    {
        typedef _Deque_iterator<_Tp, _Tp&, _Tp*>             iterator;
        typedef _Deque_iterator<_Tp, const _Tp&, const _Tp*> const_iterator;
        static size_t _S_buffer_size() { return __deque_buf_size(sizeof(_Tp)); }
        //没有继承std::iterator就必须自行撰写五个必要的迭代器相应型别
        typedef random_access_iterator_tag iterator_category;
        typedef _Tp value_type;
        typedef _Ptr pointer;
        typedef _Ref reference;
        typedef ptrdiff_t difference_type;
        
        
        typedef _Tp** _Map_pointer;
        typedef size_t size_type;
    
        typedef _Deque_iterator _Self;
    
        _Tp* _M_cur;                //迭代器缓冲区的现行元素
        _Tp* _M_first;                //缓冲区的头部
        _Tp* _M_last;                //缓冲区的尾部(包括备用空间)
        _Map_pointer _M_node;        //中控器指针
        ...
    }

      其中用来决定缓冲区大小的函数是 __deque_buf_size,是全局内联函数:

    inline size_t __deque_buf_size(size_t __size) 
    {
        //传入的元素大小__size小于512字节,则分配512 /__size 个空间,后期如果还需要,就开接着辟,直至512bytes,如果大于512bytes,就直接返回整个缓冲区
        return __size < 512 ? size_t(512 / __size) : size_t(1); }

      以下是中控器、迭代器和缓冲区的相互关系:

      

    既然选择了远方,便只顾风雨兼程
  • 相关阅读:
    Java Number类
    Java StringBuffer和StringBuilder类
    Java String类
    Java 日期时间
    Java 数组
    Java 方法
    Java正则表达式
    一步步学习SPD2010--第五章节--处理数据视图(6)--使用XSLT
    一步步学习SPD2010--第五章节--处理数据视图(5)--显示和隐藏内容
    一步步学习SPD2010--第五章节--处理数据视图(4)--应用条件格式
  • 原文地址:https://www.cnblogs.com/Forever-Road/p/6837706.html
Copyright © 2020-2023  润新知