STL(六):deque

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u014613043/article/details/52013142

隔了好多天,终于把这个deque 给写了。

deque 是双端队列。也就是,方便在队头和队尾插入数据的队列。
它能够解决vector 增加元素时不停地重新分配内存和在头部插入效率低下的问题。

看到它的数据结构才觉得真心厉害。

数据结构

看图
这里写图片描述

deque的数据组织是这样的:
有一块连续的小内存充当中控器,指向真正的数据缓冲区。有点像一本书的目录。
而数据,真正是存储在缓冲区的。
deque 主要是维护好中控器和数据的关系。

我曾经考虑,也许可以使用链表来组织这个关系,就是一块一块的缓冲区通过链表连起来。
但这样设计的问题,显而易见。理想的组织方式是把deque 封装成数组,如果用链表的话,就比较难伪装空间连续的概念。
它高明的地方就在于,把中控器当作连续的空间管理,减少了直接管理大块内存的消耗,只需要维护一个连续的小空间的中控器就行
然后在这个上面伪装成空间连续的线性表。

deque 还是比较复杂的,实现的代码超千行了。

迭代器

通过上图,也可以看到迭代器的数据结构了,数据结构中,也遵循着STL 的默认规则,即缓冲区大小是[fist, last)

比较重要的一个功能是,如何把迭代器伪装成原生的指针。因为deque 的本意是双向连续的线性数组,那么它的迭代器就是 random_iterator

这主要归功于几个重载运算符的功劳了。

template<typename T, typename Ref, typename Ptr, size_t Buff_size>
struct _deque_iterator
{
    typedef _deque_iterator<T, T&, T*, Buff_size> iterator;
    typedef _deque_iterator<T, const T&, const T*, Buff_size> const_iterator;
    typedef _deque_iterator self;

    static size_t buffer_size()
    {
        return _deque_buf_size(Buff_size, sizeof(T) );
    }

    typedef T value_type;
    typedef Ptr pointer;
    typedef Ref reference;
    typedef size_t size_type;
    typedef ptrdiff_t difference_type;
    typedef T** map_pointer;
    typedef random_access_iterator_tag iterator_category;

    pointer curr;
    pointer first;
    pointer last;
    map_pointer node;


    void set_node(map_pointer new_node)
    {
        node = new_node;
        first = *node;
        last = first + buffer_size();
    }
//-----------------function -------------
    _deque_iterator() : curr(0), first(0), last(0), node(0) {}
    _deque_iterator(pointer x, map_pointer y): node(y), first(*y), last(*y + buffer_size()), curr(x) {}
    _deque_iterator(const iterator& x): curr(x.curr), first(x.first), last(x.last), node(x.node) {}

...
}

一部分的代码如此。
我们可以看看它的重载运算符是怎么样的:


    difference_type operator - (const self& x) const 
    {
        //这个计算公式非常巧妙,值得推敲一番
        return difference_type( (node-x.node-1)*buffer_size() + (curr - first) + (x.last - x.curr) );
    }
    self& operator += (difference_type dist)
    {
        difference_type offset = dist + (curr - first);
        if(offset >=0 && offset < difference_type(buffer_size() ))
            curr += dist;
        else
        {
            //这个计算公式.....有点厉害,我承认我写的代码是垃圾=_=
            difference_type node_offset = offset >0 ? offset / difference_type(buffer_size() )
                : -difference_type( (-offset - 1)/buffer_size()) -1;
            set_node(node + node_offset);
            curr = first + (offset - node_offset * difference_type(buffer_size()) );
        }
        return *this;
    }

注意到我备注的几个公式,那是真厉害。
它通过一些运算,而规避了繁复的判断。

一些默认的行为

deque 的声明如下:

template<typename T, typename Alloc = alloc, size_t Buff_size = 0>
class deque;

它接受三个参数,其中第三个参数是指定了deque 的buffer 大小的,计算的函数如下:


/** _deque_buf_size 决定缓冲区大小的函数
    @param n 模板中的参数
    @param sz sizeof(T)
*/
inline size_t _deque_buf_size(size_t n, size_t sz)
{
    return n!=0 ? n : (sz <512 ? size_t(512/sz) : size_t(1) );  
}

还有,中控器的默认大小是8

剩下的都不怎么想写了, 反正接口规范就是C++ 官网的那些,对着实现功能就好了。
重要的是,数据组织的思想吧,一些设计上的细节也值得考究一下的。

惯例,代码在github 上,欢迎一起探讨问题。。。

–END–

展开阅读全文

没有更多推荐了,返回首页