1.deque概述
vector是单向开口的连续线性空间,而deque则是一种双向开口的连续线性空间。所谓双向开口,意思是可以在头尾两端都可以做元素的插入与删除操作。vector当然也可以在头尾两端进行操作(从技术观点),但是其头部操作其差,无法被接受。
deque和vector的最大差异,一在于deque允许于常数时间内对起头端进行元素的插入或移除操作,二在于deque没有所谓容量(capacity)观念,因为它是动态地以分段连续空间组合而成,随时可以增加一段新的时间并链接起来。
2.deque的成员函数
函数 | 描述 |
c.assign(beg,end) c.assign(n,elem) | 将[beg; end)区间中的数据赋值给c。 将n个elem的拷贝赋值给c。 |
c.at(idx) | 传回索引idx所指的数据,如果idx越界,抛出out_of_range。 |
c.back() | 传回最后一个数据,不检查这个数据是否存在。 |
c.begin() | 传回迭代器重的可一个数据。 |
c.clear() | 移除容器中所有数据。 |
deque<Elem> c deque<Elem> c1(c2) Deque<Elem> c(n) Deque<Elem> c(n, elem) Deque<Elem> c(beg,end) c.~deque<Elem>() | 创建一个空的deque。 复制一个deque。 创建一个deque,含有n个数据,数据均已缺省构造产生。 创建一个含有n个elem拷贝的deque。 创建一个以[beg;end)区间的deque。 销毁所有数据,释放内存。 |
c.empty() | 判断容器是否为空。 |
c.end() | 指向迭代器中的最后一个数据地址。 |
c.erase(pos) c.erase(beg,end) | 删除pos位置的数据,传回下一个数据的位置。 删除[beg,end)区间的数据,传回下一个数据的位置。 |
c.front() | 传回地一个数据。 |
get_allocator | 使用构造函数返回一个拷贝。 |
c.insert(pos,elem) c.insert(pos,n,elem) c.insert(pos,beg,end) | 在pos位置插入一个elem拷贝,传回新数据位置。 在pos位置插入>n个elem数据。无返回值。 在pos位置插入在[beg,end)区间的数据。无返回值。 |
c.max_size() | 返回容器中最大数据的数量。 |
c.pop_back() | 删除最后一个数据。 |
c.pop_front() | 删除头部数据。 |
c.push_back(elem) | 在尾部加入一个数据。 |
c.push_front(elem) | 在头部插入一个数据。 |
c.rbegin() | 传回一个逆向队列的第一个数据。 |
c.rend() | 传回一个逆向队列的最后一个数据的下一个位置。 |
c.resize(num) | 重新指定队列的长度。 |
c.size() | 返回容器中实际数据的个数。 |
C1.swap(c2) Swap(c1,c2) | 将c1和c2元素互换。 同上操作。 |
3.deque的设计模式
deque是连续线性空间,连续线性空间总令我们联想到array或vector,array不支持大小动态增长,而vector虽支持动态增长,但只能在尾端增长,而且其所谓的动态增加只是一种假象,事实上是(1)申请空间(2)数据移动(3)释放原空间,因此,vector的效率在有些情况下也非常低。
deque是由一段段连续的线性空间组成的。一旦有必要在deque的前端或者尾端增加新空间时,配置一段连续的线性空间将它串联在deque的头端或者尾端。所以deque的最大任务是维持这些一段段连续的线性空间整体连续的假象。
deque采用一块所谓的map(ps.不是STL中的map容器)作为主控。这里所谓的map容器就是一小块连续的线性空间,其中每个元素都是指针,指向另一块线性连续的区域,叫做缓冲区。缓冲区才是deque的储存空间的主体,代码结构如下:
template<class T,class Alloc=alloc,size_t BufSize=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内可容纳多少指针
};
4.deque的迭代器
deque是分段连续空间,为了维持deque整体连续的假象的任务,落在了迭代器operator ++和operator --的身上。deque必须掌握管控中心map以判断自己是否处于缓存区的边缘,如果是,一旦前进或者后退就可以跳转到上一个或者下一个缓存区。
template<class T,class Ptr,size_t BufSize>
struct _deque_iterator{
..................
typedef random_access_iterator_tag iterator_category;
..................
//保持与缓存区的联系
T* cur;
T* first;
T* last;
//指向管控中心
map_pointer node;
.................
};
在deque中保持着两个迭代器start和finish,迭代器start内的cur指针当然指向缓冲区的第一个元素,迭代器finish内的cur指针当然指向缓冲区内的最后元素(的下一个位置)。下面是迭代器的几个关键行为。由于迭代器内对各种指针运算都进行了重载操作,其中最关键的就是:一旦行进时遇到缓冲区边缘,可能需要调用set_node()跳一个缓冲区:
void set_node(map_pointer new_node){
node=new_node;
first=*new_node;
last=*new_node+difference_type(buffer_size());
}
在operator++ 或operator--都考虑了到达缓冲区边缘的情况,如下:
self& operator ++(){
++cur;//切换至下一个元素
if(cur==last){
set_node(node+1);
cur=first;
}
return *this;
}
5.deque的数据结构
deque除了维护一个先前说过的指向map的指针外,也维护start,finish两个迭代器,分别指向第一个缓冲区的第一个元素和最后一个缓冲区的最后一个元素的(下一个位置)。当然,它必须记住当前的map的大小。因为一旦map所提供的的节点不足,就必须重新配置一块更大的map。
template<class T,class Alloc=alloc,size_t BufSize=0>
class deque{
public:
typedef T value_type;
typedef value_type* pointer;
typedef size_t size_type;
public:
typedef __deque_iterator<T,T*,T&,BufSize> iterator;
.....
protected:
typedef pointer* map_pointer;
protected:
iterator start;
iterator finish;
map_pointer map;//指向一块连续区域,其内的每个元素都是一个指针,指向一块缓存区
size_type map_size;//map内可容纳多少指针
//成员函数
iterator begin(){
return start;
}
iterator end(){
return end;
}
,,,,,,,,,,
};