stl deque(gcc 4.8.5)

整个deque的数据结构是以数据块作为基础的,一个缓冲块限制为512 byte,实际使用过程中根据对象需要空间大小去判断该缓冲块可以容纳多少个对象,然后返回对应一个数据块的需要使用空间大小;以下是计算一个缓冲块可容纳对象的数量以及申请一个缓冲块的实现:

inline size_t __deque_buf_size(size_t __size)
{
    return (__size < _GLIBCXX_DEQUE_BUF_SIZE ? size_t(_GLIBCXX_DEQUE_BUF_SIZE / __size) : size_t(1));
}
// 申请一个缓冲块
_Tp* _M_allocate_node()
{ 
	return _M_impl._Tp_alloc_type::allocate(__deque_buf_size(sizeof(_Tp)));
}
_Tp** _M_map;               // 指向一个指针数组,其中每个指针指向一个缓冲块
size_t _M_map_size;         // 一个缓冲区是512 byte,这里描述缓冲区的个数
iterator _M_start;          // 指向整个deque最前面的位置
iterator _M_finish;         // 指向整个deque最后面的位置(卫兵)

这些数据用于描述一个deque整体的结构,一共分为两层,一层是_M_map指针数组,第二层就是各个数据块,其中_M_map中每个指针都指向一个数据块,_M_map_size为指针数组的大小;_M_start和_M_finish表示整个deque队列的始末位置,其中数据就存储在[_M_start, _M_finish)中,而deque的迭代器数据如下:

_Tp* _M_cur;
_Tp* _M_first;        // 整个缓冲块空间[_M_first, _M_last),_M_set_node指定缓冲块的时候进行指定
_Tp* _M_last;
_Map_pointer _M_node; // 缓冲块的指针是个数组,_M_node指向该数组

其中_M_cur表示当前数据块使用空间位置,[_M_first, _M_last)为数据块存储数据的空间,_M_node指向map数组,用于寻找下一个数据块位置;当找到一个数据块的起始地址,由于数据块大小明确,即可明确数据块结束位置;

当要向deque插入一些数据的时候,首先判定当前空间是否足够,不够的话就要进行空间扩展,扩展的实现就是新申请一个更大的数据块Map数组,然后将原本的Map数组指针存储到新的map数组中,然后重新指定_M_map,_M_map_size,_M_start和_M_finish;由于数据本身的载体是数据块,因此只复制map指针表相比vector效率会提升;空间处理完毕后,就对数据进行移动,为待插入数据提供空间,由于deque可以在两个方向插入,因此可以通过判断插入位置距离前后那个方向近来减少复制数据的个数,从而减少插入消耗时间,移动数据完毕后在腾出来的空间中对数据进行设置即可;

当要从deque中删除一些数据,首先就是为了保证deque中[_M_start, _M_finish)之间的数据块是充满状态的,迭代器是基于这个条件进行计算的,如下:

template<typename _Tp, typename _Ref, typename _Ptr>
inline typename _Deque_iterator<_Tp, _Ref, _Ptr>::difference_type
operator-(const _Deque_iterator<_Tp, _Ref, _Ptr>& __x,
	      const _Deque_iterator<_Tp, _Ref, _Ptr>& __y)
{
        /* 整体拆为三个部分:起始位置所在缓冲块,结束位置所在缓冲块,中间的缓冲块 */
      return typename _Deque_iterator<_Tp, _Ref, _Ptr>::difference_type
	(_Deque_iterator<_Tp, _Ref, _Ptr>::_S_buffer_size())
	* (__x._M_node - __y._M_node - 1) + (__x._M_cur - __x._M_first)
	+ (__y._M_last - __y._M_cur);
}

为了保证数据块是连续充满状态的,对于待删除的数据就要进行移动,将待删除的位置移动到deque头部或者尾部,然后进行删除即可,删除的过程和创建的过程相反,首先就是对对象的析构,然后判断数据块时候还需要,不需要就进行释放,之后对_M_start, _M_finish进行重新设置;

posted @ 2021-06-08 10:26  呵哈呵  阅读(42)  评论(0)    收藏  举报