vector是单向开口的连续线性空间,deque是双向开口(在头尾分别做元素删除和插入操作)的连续线性空间。
vector与deque的区别:
1)deque允许在头部快速进行元素插入或删除操作,而vector只允许在尾部插入元素,头部只能查看不能插入或删除
2)deque没有容量(capacity)的概念,因为它是动态地以分段连续空间组合而成的,随时可以增加一段新空间并链接起来。不会像vector那样“因旧空间不够而新配置一个更大空间,然后复制元素,再释放旧空间”。
deque的迭代器也是Ramdon Access Iterator,但它的复杂度和vector是不一样的。一般尽可能选vector。
对deque进行排序操行,为了最高效率,可将deque先复制到vector中,将vector排序后(用STL sort算法),再复制到deque。
deque的中控器
deque由一段一段的定量连续空间构成。一旦有必要在deque的前端或尾端增加新空间,便配置一段连续空间,串接在整个deque的头或尾部。deque的最大任务,便是在这些分段的定量连续空间上,维护其整体连续的假象,并提供随机存取的接口。
deque采用一块所谓的map(注意,不是STL的map容器)作为主控。这里所谓map是一小块连续空间,其中每个元素(此处称为一个节点,node)都是指针,指向另一段(较大的)连续线性空间,称为缓冲区。缓冲区才是deque的储存空间主体。SGI STL 允许我们指定缓冲区大小,默认值0表示将使用512 bytes 缓冲区。
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; //指向map,map是一个T**,也就是一个指针,其所指之物又是指针 size_type map_size; //map内可以容纳多少指针 };
deque是分段连续的,但在外面它又一直维护着整体连续的假象。我们先看看deque的数据结构:
iterator start; // 起始缓冲区 iterator finish; // 最后一个缓冲区 // 指向map, map是一个连续的空间, 其每个元素都是一个指向缓冲区的指针 // 其模型见前面的__deque_iterator map_pointer map; size_type map_size; // map容量,有多少个指针
如下是迭代器的结构:
T* cur; // 此迭代器所指之缓冲区中的现行元素 T* first; // 此迭代器所指之缓冲区的头 T* last; // 此迭代器所指之缓冲区的尾(含备用空间) map_pointer node; //指向管控中心,也就是中控器里的节点,很重要!!
此外迭代器还得完成“整体连续”的假象,如下:
/* 迭代器内对各种指针运算都进行了重载, 所以各种指针运算如加、减、前进、后退等都不能直观视之。 最关键的是:一旦遇到缓冲区边缘,要特别小心, 视前进或后退而定,可能需要调用set_node()跳一个缓冲区 */ void set_node(map_pointer new_node) { node = new_node; first = *new_node; last = first + difference_type(buffer_size()); } }; self& operator++() { ++cur; //切换到下一个元素 if (cur == last) { //如果达到了缓冲区尾端 (“缓冲区”!!注意“last”与“fininsh”) set_node(node + 1); //就切换至下一节点(也就是缓冲区) cur = first; //的第一个元素 } return *this; } // 后缀自增 // 返回当前迭代器的一个副本, 并调用前缀自增运算符实现迭代器自身的自增 self operator++(int) { self tmp = *this; ++*this; return tmp; } // 前缀自减, 处理方式类似于前缀自增 // 如果当前迭代器指向元素是当前缓冲区的第一个元素 // 则将迭代器状态调整为前一个缓冲区的最后一个元素 self& operator--() { if (cur == first) { set_node(node - 1); cur = last; } --cur; return *this; } self operator--(int) { self tmp = *this; --*this; return tmp; } //实现随机存取,迭代器可以直接跳跃n个距离 self& operator+=(difference_type n) { difference_type offset = n + (cur - first); if (offset >= 0 && offset < difference_type(buffer_size())) cur += n; //目标位置在同一缓冲区内 else { //目标位置不在同一缓冲区内 difference_type node_offset = offset > 0 ? offset / difference_type(buffer_size()) : -difference_type((-offset - 1) / buffer_size()) - 1; set_node(node + node_offset); //切换到正确的节点(缓冲区) //切换到正确的元素 cur = first + (offset - node_offset * difference_type(buffer_size())); } return *this;
deque 的内存管理
deque有自己专属的空间配置器,如下:
protected: typedef pointer* map_pointer; //专属空间配置器,每次配置一个元素大小 typedef simple_alloc<value_type, Alloc> data_allocator; //专属空间配置器,每次配置一个指针大小 typedef simple_alloc<pointer, Alloc> map_allocator; //构造函数 deque(int n, const value_type& value) : start(), finish(), map(0), map_size(0) { fill_initialize(n, value); } template <class T, class Alloc, size_t BufSize> void deque<T, Alloc, BufSize>::fill_initialize(size_type n, const value_type& value) { create_map_and_nodes(n); //把deque的结构都产生并安排好 map_pointer cur; __STL_TRY { //为每个节点的缓冲区设置初值 for (cur = start.node; cur < finish.node; ++cur) uninitialized_fill(*cur, *cur + buffer_size(), value); //最后一个缓冲区的设定不太相同 uninitialized_fill(finish.first, finish.cur, value); } # ifdef __STL_USE_EXCEPTIONS catch(...) { for (map_pointer n = start.node; n < cur; ++n) destroy(*n, *n + buffer_size()); destroy_map_and_nodes(); throw; } # endif /* __STL_USE_EXCEPTIONS */ } // 创建内部使用的map,负责安排产生并安排好deque的结构 template <class T, class Alloc, size_t BufSize> void deque<T, Alloc, BufSize>::create_map_and_nodes(size_type num_elements) { // 需要的结点数 = (元素个数 / 每个缓冲区能容纳的元素数 + 1) size_type num_nodes = num_elements / buffer_size() + 1; // map要维护的结点, 这里最小的值为8, 见initial_map_size() //最大为“所需节点数加2”,前后各预备一个,扩充时可用 map_size = max(initial_map_size(), num_nodes + 2); //配置有map_size个节点的map map = map_allocator::allocate(map_size); // 将[nstart, nfinish)区间设置在map的中间, // 这样就能保证前后增长而尽可能减少map的重新分配次数 map_pointer nstart = map + (map_size - num_nodes) / 2; map_pointer nfinish = nstart + num_nodes - 1; // 分配结点空间 map_pointer cur; __STL_TRY { //为map内的每个现用节点配置缓冲区。所有缓冲区加起来就是deque //的可用空间(最后一个缓冲区可能留有一些富裕) for (cur = nstart; cur <= nfinish; ++cur) *cur = allocate_node(); } # ifdef __STL_USE_EXCEPTIONS catch(...) { for (map_pointer n = nstart; n < cur; ++n) deallocate_node(*n); map_allocator::deallocate(map, map_size); throw; } # endif /* __STL_USE_EXCEPTIONS */ // 维护指针状态,为deque的两个迭代器start、end设置正确内容 start.set_node(nstart); finish.set_node(nfinish); start.cur = start.first; finish.cur = finish.first + num_elements % buffer_size(); } //析构函数 ~deque() { destroy(start, finish); // <stl_construct.h> destroy_map_and_nodes(); } template <class T, class Alloc, size_t BufSize> void deque<T, Alloc, BufSize>::destroy_map_and_nodes() { for (map_pointer cur = start.node; cur <= finish.node; ++cur) deallocate_node(*cur); map_allocator::deallocate(map, map_size); }
deque的元素操作:
push_back(),push_front(),pop_back(),pop_front()
void push_back(const value_type& t) { // STL使用前闭后开的区间, 所以如果缓冲区还有一个以上的备用空间, // 则直接在finish.cur上构造对象即可, 然后更新迭代器 if (finish.cur != finish.last - 1) { construct(finish.cur, t); ++finish.cur; } // 容量已满就要新申请内存了 else push_back_aux(t); } // 仅当finish.cur == finish.last - 1才调用 // 即最后一个缓冲区只剩一个备用空间才调用 // 先配置一块新缓冲区,并设置新元素内容,然后更改迭代器 template <class T, class Alloc, size_t BufSize> void deque<T, Alloc, BufSize>::push_back_aux(const value_type& t) { value_type t_copy = t; reserve_map_at_back(); //若符合某种条件则必须重换一个map *(finish.node + 1) = allocate_node(); //配置一个新节点(缓冲区) __STL_TRY { construct(finish.cur, t_copy); finish.set_node(finish.node + 1); //改变finish,令其指向新缓冲区 finish.cur = finish.first; //设定finish的状态 } __STL_UNWIND(deallocate_node(*(finish.node + 1))); } void push_front(const value_type& t) { if (start.cur != start.first) { //第一个缓冲区还有备用空间 construct(start.cur - 1, t); //直接在备用空间上构造元素 --start.cur; //调整第一缓冲区的使用状态 } else //第一缓冲区无备用空间 push_front_aux(t); } // start.cur == start.first. template <class T, class Alloc, size_t BufSize> void deque<T, Alloc, BufSize>::push_front_aux(const value_type& t) { value_type t_copy = t; reserve_map_at_front(); *(start.node - 1) = allocate_node(); //若符合某条件则必须重换一个map __STL_TRY { start.set_node(start.node - 1); //配置一个新缓冲区 start.cur = start.last - 1; construct(start.cur, t_copy); } # ifdef __STL_USE_EXCEPTIONS catch(...) { //commit or rollback:若非全部成功,就一个不留 start.set_node(start.node + 1); start.cur = start.first; deallocate_node(*(start.node - 1)); throw; } # endif /* __STL_USE_EXCEPTIONS */ } void pop_back() { if (finish.cur != finish.first) { //如果最后一个缓冲区有一个或多个元素 --finish.cur; //调整指针,相当于排除最后元素 destroy(finish.cur); //将最后元素析构 } else //最后缓冲区没元素 pop_back_aux(); //进行缓冲区的释放 } // finish.cur == finish.first. template <class T, class Alloc, size_t BufSize> void deque<T, Alloc, BufSize>:: pop_back_aux() { deallocate_node(finish.first); //释放最后一个缓冲区 finish.set_node(finish.node - 1);//调整finish的状态,指向 finish.cur = finish.last - 1; //上一个缓冲区的最后一个元素 destroy(finish.cur); } //与pop_back()类似 void pop_front() { if (start.cur != start.last - 1) { destroy(start.cur); ++start.cur; } else pop_front_aux(); } template <class T, class Alloc, size_t BufSize> void deque<T, Alloc, BufSize>::pop_front_aux() { destroy(start.cur); deallocate_node(start.first); start.set_node(start.node + 1); start.cur = start.first; }
当前后端空间不足时要进行整治:
reverse_map_at_back(),reverse_map_at_front()
void reserve_map_at_back (size_type nodes_to_add = 1) { if (nodes_to_add + 1 > map_size - (finish.node - map)) //如果map尾端的节点备用空间不够 //符合以上条件则必须重换一个map(配置更大,拷贝原来的,释放原空间) reallocate_map(nodes_to_add, false); } void reserve_map_at_front (size_type nodes_to_add = 1) { if (nodes_to_add > start.node - map) //如果map前端的节点备用空间不够 //符合以上条件则必须重换一个map(配置更大,拷贝原来的,释放原空间) reallocate_map(nodes_to_add, true); } // 重新配置map, 不会对缓冲区进行操作, map维护的是指向缓冲区的指针 template <class T, class Alloc, size_t BufSize> void deque<T, Alloc, BufSize>::reallocate_map(size_type nodes_to_add, bool add_at_front) { size_type old_num_nodes = finish.node - start.node + 1; size_type new_num_nodes = old_num_nodes + nodes_to_add; map_pointer new_nstart; if (map_size > 2 * new_num_nodes) { new_nstart = map + (map_size - new_num_nodes) / 2 + (add_at_front ? nodes_to_add : 0); if (new_nstart < start.node) copy(start.node, finish.node + 1, new_nstart); else copy_backward(start.node, finish.node + 1, new_nstart + old_num_nodes); } else { size_type new_map_size = map_size + max(map_size, nodes_to_add) + 2; map_pointer new_map = map_allocator::allocate(new_map_size); new_nstart = new_map + (new_map_size - new_num_nodes) / 2 + (add_at_front ? nodes_to_add : 0); copy(start.node, finish.node + 1, new_nstart); map_allocator::deallocate(map, map_size); map = new_map; map_size = new_map_size; } start.set_node(new_nstart); finish.set_node(new_nstart + old_num_nodes - 1); }
clear(),erase()
//该函数清除整个deque,deque初始状态(无任何元素)时,有一个 //缓冲区,因此clear后恢复初始,要保留一个缓冲区 template <class T, class Alloc, size_t BufSize> void deque<T, Alloc, BufSize>::clear() { // 以下针对头尾以外的每个缓冲区(它们是饱满的) for (map_pointer node = start.node + 1; node < finish.node; ++node) { //将缓冲区内所有元素析构 destroy(*node, *node + buffer_size()); //释放缓冲区内存 data_allocator::deallocate(*node, buffer_size()); } // 至少有头尾两个缓冲区 if (start.node != finish.node) { destroy(start.cur, start.last); //将头缓冲区目前的元素析构 destroy(finish.first, finish.cur); //将尾缓冲区目前元素析构 //!!!注意:以下释放尾缓冲区,头缓冲区保留 data_allocator::deallocate(finish.first, buffer_size()); } // 析构所有元素, 但是不释放空间, 因为deque要满足这个前置条件 // 具体的细节见本文件开头'特性' else destroy(start.cur, finish.cur); finish = start; //调整状态 } template <class T, class Alloc, size_t BufSize> deque<T, Alloc, BufSize>::iterator deque<T, Alloc, BufSize>::erase(iterator first, iterator last) { //清楚的是整个deque,直接调用clear() if (first == start && last == finish) { clear(); return finish; } else { difference_type n = last - first; //清除区间长度 difference_type elems_before = first - start; //清楚区间前方的元素 //判断清除区间前后哪个元素少 if (elems_before < (size() - n) / 2) { //如果前方元素少,则将前方元素 copy_backward(start, first, last); //后移(覆盖清除区间) iterator new_start = start + n; //标记deque新起点 destroy(start, new_start); //移动完毕,将冗余元素析构 //将冗余缓冲区释放 for (map_pointer cur = start.node; cur < new_start.node; ++cur) data_allocator::deallocate(*cur, buffer_size()); start = new_start; //设定deque新起点 } else { //清除区间后方元素少,向前移动后方元素(覆盖清除区) copy(last, finish, first); iterator new_finish = finish - n; destroy(new_finish, finish); for (map_pointer cur = new_finish.node + 1; cur <= finish.node; ++cur) data_allocator::deallocate(*cur, buffer_size()); finish = new_finish; } return start + elems_before; } }
insert()
template <class T, class Alloc, size_t BufSize> void deque<T, Alloc, BufSize>::insert(iterator pos, const_iterator first, const_iterator last) { size_type n = last - first; if (pos.cur == start.cur) { iterator new_start = reserve_elements_at_front(n); __STL_TRY { uninitialized_copy(first, last, new_start); start = new_start; } __STL_UNWIND(destroy_nodes_at_front(new_start)); } else if (pos.cur == finish.cur) { iterator new_finish = reserve_elements_at_back(n); __STL_TRY { uninitialized_copy(first, last, finish); finish = new_finish; } __STL_UNWIND(destroy_nodes_at_back(new_finish)); } else insert_aux(pos, first, last, n); } template <class T, class Alloc, size_t BufSize> typename deque<T, Alloc, BufSize>::iterator deque<T, Alloc, BufSize>::insert_aux(iterator pos, const value_type& x) { difference_type index = pos - start; //插入点之前的元素个数 value_type x_copy = x; if (index < size() / 2) { //如果插入点前元素个数少 push_front(front()); //在最前端加入与第一个元素同值的元素 iterator front1 = start; ++front1; iterator front2 = front1; ++front2; pos = start + index; iterator pos1 = pos; ++pos1; copy(front2, pos1, front1); //元素移动 } else { //插入点后的元素个数较少 push_back(back()); //在尾部插入一个与最后一个元素同值的元素 iterator back1 = finish; --back1; iterator back2 = back1; --back2; pos = start + index; copy_backward(pos, back2, back1); //元素移动 } *pos = x_copy; //在插入点上设定新值 return pos; }