我们都知道在使用deque的时候,它是两端都可插入,取出;并且支持随机取数的一个容器;给人从表面上看起来,好像是一个连续线性空间;其实deque的内部实现上,它是一个分段连续线性空间。
deque和Vector的最大差异,一在于deque允许于常数时间内对头端进行元素的插入或移除操作,(这里就是因为分段的原因,它只需要在这一小段进行操作,不像vector是整个区间向后移动);二在于deque没有所谓容量观念,因为它是动态地以分段连续空间组合而成,(于是就没必要想vector一样,弄一个capacity,来判断空间容量,以此来成倍扩容,移动)
就是由于分段连续空间,从而这里的迭代器就不像普通指针,其复杂度就会升高;
下面体会一下设计的分层思想
一、deque的中控器
我们知道,现在deque是用一块块连续的空间拼接而成,从而,如果一个迭代器走到了其中一块的尾端,那么它就需要跨到另一个段,因此这里就需要一个中控器,得记录每一块的地址,这样才能方便移动;
这里就是STL中大体的一个结构,可以从图中看出,其中控器是一个叫map的一块连续空间控制的;里面每个节点放置的是一块存储区的头节点;
template <class T,class Alloc=alloc,size_t BufSiz=0>
class deque{
public :
typedef T value_type;
typedef value_type* pointer;
...
protected:
typedef pointer* map_pointer;
protected:
map_pointer map;
size_type map_size;
}
因此,map存的是一个地址,而它本身也是一个地址;
于是,我就在想,这要是让我接下来,粗略的去设计怎么移动,怎么放置数据,我应该怎么设计
我想的是,我的缓冲区里,固定同样大小段的尺寸,这样我就在map里面存放首地址,当取某个元素的时候,就计算一下在哪一个块,走多少步即可,而头部插入,我想的是想栈一样,放入缓存块的尾部,依次从尾部,向头部增长(很明显,这里我并没有想好,因为我的map放的是首地址,而头部插入我要是放在尾部,那你根本不知道第一个数据在哪?这样,我就需要多一个指针来维护地一个地址和最后一个地址,不过现在想一下,的确也可以)
接下来就是deque的两层设计,因为外表的整体连续,从而就需要一个指针来维护其跨块和在一个块内的移动,这就是
二、迭代器
//简单的表示
//这里利用first,last来控制一块的边界
//用node来进行下一块的,或者上一块的移动
template <class T>
struct __deque_iterator
{
T *cur; 此迭代器指向缓冲区的当前元素
T *first; //此迭代器指向一块缓冲区的头部
T *last; //此迭代器指向一块缓冲区的尾部
map_pointer node; //指向管控中心
};
因此,有了迭代器,那么就可以实现元素的移动,比如operator++,operator--;operator-=等等;
三、deque的数据结构
template <class T>
class deque{
public:
typedef T value_type;
typedef value_type* pointer;
typedef size_t size_type;
public:
typedef __deque_iterator<T> iterator;
protected:
typedef pointer* map_pointer;
protected:
iterator start;
iterator finish;
map_pointer map;
size_type map_size;
}
通过start 和 finish 来指向首尾元素的节点。(当然其中包含了它所在块的头和尾,和在map中的节点)
有了这些结构从而就可以实现比如:
public:
iterator begin() {return start;}
iterator end() {return finish;}
reference operator[](size_type n){
return start[difference_type(n)];
}
reference front() {return *start;}
reference back(){
iterator tmp=finish;
--temp;
return *temp;
//这里为什么不使用return *(finish-1);
//因为这里并没有定义iterator-int的算子
}
最后,那么接下来就需要创建map和node了,以及push_back(),push_front()操作,这些都是逻辑操作,就不赘述;当然还有一下需要注意的问题,比如什么时候map中给的节点并不够了,那么就需要换一块连续的空间,来存储块的地址;