Chapter 4:序列式容器之 deque
2017-01-11 19:36
344 查看
1:deque 与 vector 的区别
deque 与 vector 的最大差异在于以下两点:deque 允许于常数时间内对起头端进行元素的插入与删除操作;
deque 的空间是动态地以分段连续空间组合而成,随时可以增加一段新的空间链接起来,因此 deque 没有所谓的容量(capacity)概念,而 vector 被分配的空间是一段连续空间
因此 像 vector 那样“因旧空间不足而重新配置一块更大的空间,然后复制元素,再释放旧空间”这样的事情在 deque 是不会发生的
2:deque 的中控器
1):deque 系由一段一段的定量连续空间组成。一旦有必要在 deque 的前端或尾端增加新空间,便配置一段定量线性空间,串接在整个 deque 的头端或尾端;2):deque 采用一段所谓的 map(不是 STL 中的 map 容器)作为主控来维护 deque 整体连续的假象。这里 map 是一小块连续空间,其中每个元素(称之为节点,node)都是指针,指向另一端(较大的)连续线性空间,称之为缓冲区,此缓冲区才是 deque 的存储空间主体。
map 的代码如下:
template <class T, class Alloc = alloc, size_t BufSiz = 0> class deque { public: //Basic types typedef T value_type; typedef value_type* pointer; typedef size_t size_type; ... protected: //Internal types //元素的指针的指针 typedef pointer* map_pointer; protected: map_pointer map; //指向 map,map 是块连续空间,其内的每个元素 // 都是一个指针(称为节点),指向一块缓冲区 size_type map_size; // map 内可容纳多少指针 ... };
3:deque 的迭代器
1:deque 迭代器属于 Random Access Iterator,代码如下: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)); } template <class T, class Ref, class Ptr, size_t BufSiz> struct __deque_iterator { typedef __deque_iterator<T, T&, T*, BufSiz> iterator; typedef __deque_iterator<T, const T&, const T*, BufSiz> const_iterator; static size_t buffer_size() { return __deque_buf_size(BufSiz, sizeof(T)); } typedef random_access_iterator_tag iterator_category; 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 __deque_iterator self; T* cur; //此迭代器所指之缓冲区的现行(current)元素 T* first; //此迭代器所指之缓冲区的头 T* last; //此迭代器所指之缓冲区的尾(含备用空间) map_pointer node; //指向 map(管理缓冲区地址) void set_node(map_pointer new_node) { node = new_node; first = *new_node; last = first + difference_type(buffer_size()); } reference operator*() const { return *cur; } pointer operator->() const { return &(operator*()); } difference_type operator-(const self& x) const { return difference_type(buffer_size())*(node - x.node -1) + (cur - first) + (x.last - x.cur); } self& operator++() { ++cur; if (cur == last) { 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; } self operator+(difference_type n) const { self tmp = *this; return tmp += n; //调用 operator+= } self& operator-=(difference_type n) { return *this += -n; } //利用 operator+= 来完成 operator-=; self operator-(difference_type n) const { self tmp = *this; return tmp -= n; //调用 operator-= } //以下实现随机存取,迭代器可以直接跳跃 n 个距离 reference operator[](difference_type n) const { return *(*this + n); } bool operator==(const self& x) const { return cur == x.cur; } bool operator!=(const self& x) const { return !(*this == x); } bool operator<(const self& x) const { return (node == x.node) ? (cur < x.cur) : (node < x.node); } };
4:deque 的数据结构
1):deque 需要维护一个指向 map 的指针,也需要维护 start 和 finish 两个迭代器,分别指向第一缓冲区的第一个元素和最后缓冲区最后一个元素的下一个位置,此外还必须记住目前 map 的大小,因为一旦 map 所提供的节点不足,就必须重新配置更大一块 map;代码如下:template <class T, class Alloc = alloc, size_t BufSiz = 0> class deque { public: //Basic types typedef T value_type; typedef value_type* pointer; typedef size_t size_type; typedef T& reference; typedef __deque_iterator<T, T&, T*, BufSiz> iterator; typedef __deque_iterator<T, const T&, const T*, BufSiz> const_iterator; protected: //Internal types //元素的指针的指针 typedef pointer* map_pointer; protected: iterator start; //指向第一个节点 iterator finish; //指向最后一个节点 map_pointer map; //指向 map,map 是块连续空间,其内的每个元素 // 都是一个指针(称为节点),指向一块缓冲区 size_type map_size; // map 内可容纳多少指针 ... };
2):有了上述结构,以下数个操作便可轻易完成:
public: iterator begin() { return start; } const_iterator begin() const { return const_iterator{start.cur, start.first, start.last, start.node}; } iterator end() { return finish; } const_iterator end() const { return const_iterator{finish.cur, finish.first, finish.last, finish.node}; } reference operator[](size_type n) { return start[difference_type(n)]; } const reference operator[](size_type n) const { return start[difference_type(n)]; } reference front() { return *start; } const reference front() const { return *start; } reference back() { iterator tmp = finish; --tmp; return *tmp; } const reference back() const { iterator tmp = finish; --tmp; return *tmp; } size_type size() const { return finish - start; } size_type max_size() const { return size_type(-1); } bool empty() const { return finish == start; }
5:deque 的构造与内存管理 constructor, push_back 和 push_front
1):deque 自行定义了两个专属的空间配置器:protected: //专属之空间配置器,每次配置一个元素大小 typedef simple_alloc<value_type, Alloc> data_allocator; //专属之空间配置器,每次配置一个指针大小 typedef simple_alloc<pointer, Alloc> map_allocator;
2):deque 提供了一个如下的 constructor:
deque(int n, const value_type& value) : start(), finish(), map(0), map_size(0) { fill_initialize(n, value); }
其中
fill_initialize()函数代码如下:
protected: void fill_initialize(size_type n, const value_type& value){ create_map_and_nodes(n); //把deque的结构都安排好 map_pointer cur; //为每个节点的缓冲区设定初值 for (cur = start.node; cur != finish.node; ++cur) uninitialized_fill(*cur, *cur + buffer_size(), value); //最后一个 uninitialized_fill(finish.first, finish.cur, value); } void create_map_and_nodes(size_type num_elemenets) { //如果刚好整除,就多配一个节点 size_type num_nodes = num_elements / buffer_size() + 1; //一个 map 要管理几个节点,最少 8 个,最多是"所需节点数加2“ //(前后各预留一个,扩充时用) map_size = max(initialize_map_size(), num_nodes + 2); map = map_allocator::allocate(map_size); map_pointer nstart = map + (map_size - num_nodes) / 2; map_pointer nfinish = nstart + num_nodes - 1; map_allocator cur; for (cur = nstatr; cur <= nfinish; ++cur) *cur = data_allocator::allocate(buffer_size()); start.set_node(nstart); start.cur = start.first; finish.set_node(nfinish); finish.cur = finish.first + num_elements % buffer_size(); }
3):deque 提供了一个
push_back()和
push_front()函数 ,用于在尾端和前端插入,代码如下:
public: void push_back(const value_type& val) { if (finish.cur != finish.last - 1) { //最后缓冲区尚 有两个(含)以上的元素备用空间 construct(finish.cur, val) ++finish.cur; } else //最后缓冲区只剩一个元素备用空间 push_back_aux(val); } void push_front(const value_type& val) { if (start.cur != start.first){ //第一缓冲区尚有备用空间 construct(start.cur - 1, val); //直接在备用空间上构造元素 --start.cur; //调整第一缓冲区的使用状态 } else //第一缓冲区没有备用空间 push_front_aux(val); }
其中
push_back_aux()和
push_front_aux()函数的代码如下:
protected: //只有当最后一个缓冲区只剩一个备用元素时才会被调用 void push_back_aux(const value_type& val) { value_type val_copy = val; reserve_map_at_back(); //若符合某种条件则必须重换一个 map *(finish.node + 1) = data_allocator::allocate(bufer_size()); try { construct(finish.cur, val_copy); finish.set_node(finish.node + 1); finish.cur = finish.first; } catch(...) data_allocator::deallocate(*(finish.node + 1)); } //只有当第一缓冲区没有任何备用元素时才会被调用 void push_front_aux(const value_type& val) { value_type val_copy = val; reserve_map_at_front(); *(start - 1) = data_allocator::allocate(bufer_size()); try { start.set_node(start.node - 1); start.cur = start.last -1; construct(start.cur, val_copy) } catch { start.set_node(start.node + 1); start.cur = start.first; data_allocator::deallocate(start.node - 1, bufer_size()); } }
函数
reserve_map_at_back()和
reserve_map_at_front()函数代码如下:
protected: void reserve_map_at_back(size_type nodes_to_add = 1) { if (nodes_to_add > map_size - (finish.node - map + 1)) reallocate_map(nodes_to_add, false); } void reserve_map_at_front(size_type nodes_to_add = 1) { if (nodes_to_add > start.node - map) reallocate_map(nodes_to_add, true); } void 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_start); 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 使用 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); //把原 map 内容拷贝过来 copy(start.node, finish.node + 1, new_nstart); //释放原 map map_allocator::deallocate(map, map_size); map = new_map; map_size = new_map_size; } //重新设定迭代器 start 和 finish start.set_node(new_start); finish.set_node(new_start + old_num_nodes - 1); }
6:deque 的其它一些操作pop_back(), pop_front(), clear(), erase(), insert()
1):pop_back()函数用于删除 deque 的最后一个元素,代码如下:
public: void pop_back() { if (finish.cur != finish.first) { //最后缓冲区有一个(或更多)元素 --finish.cur; destroy(finish.cur); } else //最后缓冲区没有任何元素 pop_back_aux(); //这里进行缓冲区的释放工作 }
其中
pop_back_aux()函数代码如下:
protected: //只有当最后缓冲区没有元素时才被调用 void pop_back_aux() { data_allocator::deallocate(finish.first, bufer_size()); finish.set_node(finsih.node - 1); finish.cur = finish.last - 1; destroy(finish.cur); }
2):
pop_front()函数用于删除 deque 的第一个元素,代码如下:
public: void pop_front() { if (start.cur != start.last - 1) { //第一缓冲区有两个或更多的元素 destroy(start.cur); ++start.cur; } else //第一缓冲区仅有一个元素 pop_front_aux(); }
其中
pop_front_aux()函数代码如下:
protected: //只有当第一缓冲区只有一个元素时才被调用 void pop_front_aux() { destroy(start.cur); data_allocator::deallocate(start.first, bufer_size()); start.set_node(start.node + 1); start.cur = start.first; }
3):
clear()函数用来清除整个 deque;由于 deque 的最初状态(无任何元素时)保有一个缓冲区,因此,
clear()完成之后也需要保留一个缓冲区以恢复到最初的状态,代码如下:
public: //因为 deque 的最初状态(无任何元素时)保有一个缓冲区,因此 //clear() 之后也需要留下一个缓冲区 void 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_allocae::deallocate(finish.first, buffer_size()); } else //只有一个缓冲区 destroy(start.cur, finis.cur); //调整状态 start.cur = start.first; finish = start; }
4):
erase()函数有两个版本,一个版本用来清除某一个位置的元素,一个版本用来清除 [first, last) 区间的所有元素,代码如下:
public: //清除 pos 所指的元素,pos 为清除点 iterator erase(iterator pos) { iterator next = pos; ++next; difference_type index = pos - start; //清除点之前的元素个数 if (index < (size() / 2)) { copy_backward(start, pos, next); pop_front(); } else { copy(next, finish, pos); pop_back(); } return start + index; } //清除 [first, last) 区间内的元素 iterator erase(iterator first, iterator last) { if (first == start && last == finish) { //如果清除区间就是整个 deque 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) dara_allocator::deallocate(*cur, buffer_size()); start = new_start; //设定 deque 的新起点 } else { //如果清除区间后方的元素比较少 copy(last, finish, first); //向前移动后方元素(覆盖清除区间) iterator new_finish = finish - n; //标记 deque 的新尾点 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; //设定 deque 的新尾点 } return start + elems_before; } }
5):
iterator insert(iterator position, const T& x)表示在某个点position(之前)插入一个元素,并设定其值;代码如下:
public: //在 position 之前插入一个元素,其值为 x; iterator insert(iterator position, const value_type& x) { if (position.cur == start.cur) { //插入点是 deque 的最前端 push_front(x); return start; } else if (position.cur == finish.cur) { //插入点是 deque 的最尾端 push_back(x); iterator tmp = finish; --tmp; return tmp; } else { //交给 insert_aux 去做 return insert_aux(position, x); } }
其中辅助插入函数
insert_aux()代码如下:
protected: iterator 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; }
相关文章推荐
- 序列式容器vector和deque
- STL序列式容器 - deque
- STL源码笔记(12)—序列式容器之deque(二)
- 《STL源码剖析》学习笔记之四——序列式容器(deque之一)
- STL中序列式容器之三deque
- STL学习——STL中的序列式容器及适配器总结(vector、list、deque、stack、queue)
- STL源码剖析-序列式容器之deque
- STL源码笔记(11)—序列式容器之deque(一)
- C++序列式容器vector,deque,list
- 序列式容器——vector,deque,list
- Chapter 4: 序列式容器之 heap and priority queue
- 《STL源码剖析》深入分析序列式容器——deque
- Chapter 4: 序列式容器之 vector
- STL容器-序列式容器deque
- STL源码剖析之序列式容器Deque
- STL学习笔记——序列式容器deque
- Chapter 4: 序列式容器之 stack 和 queue
- C++ STL学习之三:序列式容器deque深入学习
- 三 序列式容器(三)deque
- Chapter 8.顺序容器deque