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); }

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

  

既然选择了远方,便只顾风雨兼程
原文地址:https://www.cnblogs.com/Forever-Road/p/6837706.html