标签:
一、deque的中控器
deque是连续空间(至少逻辑上看来如此),连续线性空间总令我们联想到array或vector。array无法成长,vector虽可成长,却只能向尾端成长,而且其所谓的成长原是个假象,事实上是(1)另觅更大空间;(2)将原数据复制过去;(3)释放原空间三部曲。如果不是vector每次配置新空间时都有留下一些余裕,其成长假象所带来的代价将是相当高昂。
deque系由一段一段的定量连续空间构成。一旦有必要在deque的前端或尾端增加新空间,便配置一段定量连续空间,串接在整个deque的头端或尾端。deque的最大任务,便是在这些分段的定量连续空间上,维护其整体连续的假象,并提供随机存取的借口。避开了“重新配置、复制、释放”的轮回,代价则是复杂的迭代器架构。
受到分段连续线性空间的字面影响,我们可能以为deque的实现复杂度和vector相比虽不中亦不远矣,其实不然。主要因为,既是分段连续线性空间,就必须有中央控制,而为了维持整体连续的假象,数据结构的设计及迭代器前进后退等操作都颇为繁琐。deque的实现代码分量远比vector或list都多得多。
deque采用一块所谓的map(注意,不是STL的map容器)作为主控。这里所谓map是一小块连续空间,其中每个元素(此处称为一个节点,node)都是指针,指向另一段(较大的)连续线性空间,称为缓冲区。缓冲区才是deque的储存空间主体。SGI STL 允许我们指定缓冲区大小,默认值0表示将使用512 bytes 缓冲区。
二、deque的迭代器
让我们思考一下,deque的迭代器应该具备什么结构,首先,它必须能够指出分段连续空间(亦即缓冲区)在哪里,其次它必须能够判断自己是否已经处于其所在缓冲区的边缘,如果是,一旦前进或后退就必须跳跃至下一个或上一个缓冲区。为了能够正确跳跃,deque必须随时掌握管控中心(map)。所以在迭代器中需要定义:当前元素的指针,当前元素所在缓冲区的起始指针,当前元素所在缓冲区的尾指针,指向map中指向所在缓区地址的指针。
在进行迭代器的移动时,需要考虑跨缓冲区的情况。
重载前加(减),在实现后加(减)时,调用重载的前加(减)。
重载+=,实现+时,直接调用+=,实现-=时,调用+=负数,实现-时,调用-=.
//当需要实现新的功能时,最好使用已经重载好的操作,即方便有安全。。。。
另外,deque在效率上来说是不够vector好的,因此有时候在对deque进行sort的时候,需要先将元素移到vector再进行sort,然后移回来。
构造函数:根据缓冲区设置大小和元素个数,决定map的大小;给map分配空间,根据缓冲区的个数,分配缓冲区,默认指定一个缓冲区;
设置start和finish迭代器,满足左闭右开的原则。
push_back:如果空间满足,直接插入;不满足,调用push_back_aux。
push_back_aux:先调用reverse_map_at_back,若符合某种条件,重换一个map;分配空间。
reserve_map_at_back:看看map有没有满,满的话,调用reallocate_map。
reallocate_map:如果前端或后端pop过多,就会导致大量的空闲空间,如果是这种情况,则不用新分配空间,调整一下start的位置即可;
如果不够,则需要重新申请空间。
pop:析构元素,如果是最后一块还需要删除空间。
erase:需要判断,前面的元素少还是后面的元素少,移动较少的部分。
insert:判断位置,如果为前端或后端直接调用push操作,否则,移动较少的一端。
deque的构造与内存管理:
由于deque的设计思想就是由一块块的缓存区连接起来的,因此它的内存管理会比较复杂。插入的时候要考虑是否要跳转缓存区、是否要新建map节点(和vector一样,其实是重新分配一块空间给map,删除原来空间)、插入后元素是前面元素向前移动还是后面元素向后面移动(谁小移动谁)。而在删除元素的时候,考虑是将前面元素后移覆盖需要移除元素的地方还是后面元素前移覆盖(谁小移动谁)。移动完以后要析构冗余的元素,释放冗余的缓存区。
部分内容转自:STL源码剖析
//
// Deque.h
// deque
//
// Created by 颜泽鑫 on 6/3/16.
// Copyright ? 2016 颜泽鑫. All rights reserved.
//
/*
template <typename T> // template declaration.
class DequeIterator {
public:
typedef DequeIterator iterator;
typedef DequeIterator const_iterator;
typedef random_access_iterator_tag iterator_category;
// iterator tag.
typedef T value_type;
typedef T* pointer;
typedef T& reference;
typedef size_t size_type;
typedef ptrdiff_t difference_type;
typedef T** map_pointer;
typedef DequeIterator self;
T* cur;
// pointer, pointing to the current element.
T* first;
// pointer, pointing to the first element in current buffer.
T* last;
// pointer, pointing to the last element in current buffer.
map_pointer node;
// pointer, pointing to the buffer.
public: // constructor
DequeIterator(Tx x, map_pointer y);
DequeIterator();
DequeIterator(const iterator& x);
public:
// basic operation
reference operator*() const;
reference operator->() const;
void set_node(map_pointer new_node);
difference_type operator-(const self& x) const;
// logic operation
bool operator==(const self& x);
bool operator!=(const self& x);
bool operator<(const self& x);
// random access
self& operator++();
self operator++(int);
self& operator--();
self operator--(int);
self& operator+=(difference_type n);
self& operator-=(difference_type n);
self operator+(difference_type n) const;
self operator-(difference_type n) const;
reference operator[](differece_type n) const;
*/
#ifndef DequeIterator_
#define DequeIterator_
#include <iostream>
#define BUFFER_SIZE 10
using namespace std;
template <typename T>
class DequeIterator {
public:
typedef DequeIterator iterator;
typedef DequeIterator const_iterator;
typedef random_access_iterator_tag iterator_category;
// iterator tag.
typedef T value_type;
typedef T* pointer;
typedef T& reference;
typedef size_t size_type;
typedef ptrdiff_t difference_type;
typedef T** map_pointer;
typedef DequeIterator self;
T* cur; // pointer, pointing to the current element.
T* first; // pointer, pointing to the first element in current buffer.
T* last; // pointer, pointing to the last element in current buffer.
map_pointer node; // pointer, pointing to the buffer.
static size_t buffer_size() { return BUFFER_SIZE; }
DequeIterator(T* x, map_pointer y)
: cur(x), first(*y), last(*y + buffer_size()), node(y) {}
DequeIterator() : cur(0), first(0), last(0), node(0) {}
DequeIterator(const iterator& x)
: cur(x.cur), first(x.first), last(x.last), node(x.node) {}
// return *current;定义解引用操作
reference operator*() const { return *cur; }
// return current;定义箭头操作符
reference operator->() const { return cur; }
// 判断两个迭代器是否相等
bool operator==(const self& x) const { return cur == x.cur; }
// 判断两个迭代器是否不相等
bool operator!=(const self& x) const { return !(*this == x); }
// 先比较map的node,在比较cur
bool operator<(const self& x) const {
return (node == x.node) ? (cur < x.cur) : (node < x.node);
}
// 将当前的迭代器设置为new_node,主要是设置node、first、last属性的值
void set_node(map_pointer new_node) {
node = new_node;
first = *new_node;
last = first + BUFFER_SIZE;
}
difference_type operator-(const self& x) const {
return difference_type(buffer_size()) * (node - x.node - 1) +
(cur - first) + (x.last - x.cur);
}
// 定义前置自加,++p,返回引用,因为是return *this
self& operator++() {
++cur;
if (cur == last) {
set_node(node + 1);
cur = first;
}
return *this;
}
// 定义后置自加,p++,返回非引用,因为是return
self operator++(int) {
self temp = *this;
++*this;
return temp;
}
// 定义前置自减,--p,返回引用
self& operator--() {
if (cur == first) {
set_node(node - 1);
cur = last;
}
--cur;
return *this;
}
// 定义后置自减,p--,返回非引用
self operator--(int) {
self temp = *this;
--*this;
return temp;
}
// 将本迭代器自加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) { return * this += -n; }
self operator+(difference_type n) const {
self tmp = *this;
// 这里调用了operator +=()可以自动调整指针状态
return tmp += n;
}
self operator-(difference_type n) const {
self tmp = *this;
return tmp -= n;
}
reference operator[](difference_type n) const { return *(*this + n); }
operator T*() { return (*this).cur; }
};
#endif /* DequeIterator */
/*
#include <new>
#define MAP_SIZE 20
template <typename T>
class deque {
public:
typedef T value_type;
typedef value_type* pointer;
typedef value_type& reference;
typedef size_t size_type;
typedef ptrdiff_t difference_type;
typedef allocator<value_type> d_allocator;
typedef allocator<pointer> m_allocator;
typedef DequeIterator<T> iterator;
typedef pointer* map_pointer;
m_allocator map_allocator;
d_allocator data_allocator;
protected:
static size_type buffer_size();
static size_type initial_map_size();
size_type max_size() const;
protected: // memeory operation
pointer allocate_node();
void deallocate_node(pointer n);
void create_map_and_nodes(size_type num_elements);
void reallocate_map(size_type nodes_to_add, bool add_at_front);
void destroy_map_and_nodes();
void reserve_map_at_back(size_type nodes_to_add = 1);
void reserve_map_at_front(size_type nodes_to_add = 1);
protected:
iterator start;
iterator finish;
map_pointer map;
size_type map_size;
public:
deque();
~deque();
iterator begin();
iterator end();
reference front();
reference back();
reference operator[](size_type n);
size_type size() const;
bool empty() const;
public:
// elements operation
void push_back(const value_type& value);
void push_back_aux(const value_type& value);
void push_front(const value_type& value);
void push_front_aux(const value_type& value);
void pop_back();
void pop_front();
void pop_back_aux();
void pop_front_aux();
void insert(iterator pos, const value_type& value = value_type());
void insert_aux(iterator pos, const value_type& value);
iterator erase(iterator pos);
iterator erase(iterator first, iterator last);
clear();
};
*/
#ifndef Deque
#define Deque
#include <new>
#define MAP_SIZE 20 // we set the size of map is 20
template <typename T>
class deque {
public:
typedef T value_type;
typedef value_type* pointer;
typedef value_type& reference;
typedef size_t size_type;
typedef ptrdiff_t difference_type;
typedef allocator<value_type> d_allocator;
typedef allocator<pointer> m_allocator;
typedef DequeIterator<T> iterator;
typedef pointer* map_pointer;
m_allocator map_allocator;
d_allocator data_allocator;
protected:
static size_type buffer_size() { return size_t(BUFFER_SIZE); }
static size_type initial_map_size() { return 8; }
size_type max_size() const { return size_type(-1); }
// 内存分配和回收的相关函数
// 为node分配空间
pointer allocate_node() { return data_allocator.allocate(buffer_size()); }
// 为node回收空间
void deallocate_node(pointer n) {
data_allocator.deallocate(n, buffer_size());
}
// 创造map和节点node
void create_map_and_nodes(size_type num_elements) {
size_type num_nodes = num_elements / BUFFER_SIZE + 1;
map_size = max(initial_map_size(), num_nodes + 2);
// 开头结尾各多增加一个,头尾插入时更方便。
map = map_allocator.allocate(map_size);
// 在中间设置start和finish
map_pointer nstart = map + (map_size - num_nodes) / 2;
map_pointer nfinish = nstart + num_nodes - 1;
for (map_pointer cur = nstart; cur <= nfinish; cur++) {
*cur = allocate_node();
}
start.set_node(nstart);
finish.set_node(nfinish);
start.cur = start.first;
finish.cur = finish.first + num_elements % BUFFER_SIZE;
}
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) {
// 用于平衡头尾的node
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使用。
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的起始位址與大小
map = new_map;
map_size = new_map_size;
}
// 重新設定迭代器 start 和 finish
start.set_node(new_nstart);
finish.set_node(new_nstart + old_num_nodes - 1);
}
protected:
iterator start;
iterator finish;
map_pointer map;
size_type map_size;
public:
deque() : start(), finish(), map(0) { create_map_and_nodes(0); }
~deque() {
for (map_pointer node = start.node; node <= finish.node; ++node) {
// 释放缓冲区内存
delete *node;
}
destroy_map_and_nodes();
}
void destroy_map_and_nodes() { map_allocator.deallocate(map, map_size); }
iterator begin() { return start; }
iterator end() { return finish; }
reference front() { return *start; }
reference back() {
iterator temp = finish;
--temp;
return *temp;
}
reference operator[](size_type n) { return start[difference_type(n)]; }
size_type size() const { return finish - start; }
bool empty() const { return finish == start; }
void push_back(const value_type& t) {
// 最后缓冲区尚有两个(含)以上的元素备用空间
if (finish.cur != finish.last - 1) {
data_allocator.construct(finish.cur, t); // 直接在备用空间上构造元素
++finish.cur; // 调整最后缓冲区的使用状态
} else {
// 容量已满就要新申请内存了
push_back_aux(t);
}
}
void push_back_aux(const value_type& t) {
value_type t_copy = t;
reserve_map_at_back();
*(finish.node + 1) = allocate_node(); // 配置一个新节点(缓冲区)
map_allocator.construct(finish.cur, t_copy); // 针对标的元素设值
finish.set_node(finish.node + 1); // 改变finish,令其指向新节点
finish.cur = finish.first; // 设定finish的状态
}
void reserve_map_at_back(size_type nodes_to_add = 1) {
if (nodes_to_add > map_size - (finish.node - map) - 1)
// 如果 map 尾端的節點備用空間不足
// 符合以上條件則必須重換一個map(配置更大的,拷貝原來的,釋放原來的)
reallocate_map(nodes_to_add, false);
}
void push_front(const value_type& t) {
if (start.cur != start.first) // 第一缓冲区尚有备用空间
{
data_allocator.construct(start.cur - 1, t); // 直接在备用空间上构造元素
--start.cur; // 调整第一缓冲区的使用状态
} else // 第一缓冲区已无备用空间
push_front_aux(t);
}
void push_front_aux(const value_type& t) {
value_type t_copy = t;
reserve_map_at_front();
*(start.node - 1) = allocate_node();
start.set_node(start.node - 1); // 改变start,令其指向新节点
start.cur = start.last - 1; // 设定start的状态
data_allocator.construct(start.cur, t_copy); // 针对标的元素设值
}
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);
}
void pop_back() {
if (!empty()) {
if (finish.cur != finish.first) // 最后缓冲区有一个(或更多)元素
{
--finish.cur; // 调整指针,相当于排除了最后元素
data_allocator.destroy(finish.cur); // 将最后元素析构
} else
// 最后缓冲区没有任何元素
pop_back_aux(); // 这里将进行缓冲区的释放工作
}
}
void pop_back_aux() {
deallocate_node(finish.first); // 释放最后一个缓冲区
finish.set_node(finish.node - 1); // 调整finish状态,使指向
finish.cur = finish.last - 1; // 上一个缓冲区的最后一个元素
data_allocator.destroy(finish.cur); // 将该元素析构
}
void pop_front() {
if (!empty()) {
if (start.cur != start.last - 1) // 第一缓冲区有两个(或更多)元素
{
data_allocator.destroy(start.cur); // 将第一元素析构
++start.cur; //调整指针,相当于排除了第一元素
} else
// 第一缓冲区仅有一个元素
pop_front_aux(); // 这里将进行缓冲区的释放工作
}
}
void pop_front_aux() {
data_allocator.destroy(
start.cur); // 将第一个缓冲区的第一个(也是最后一个、唯一一个)元素析构
deallocate_node(start.first); // 释放第一缓冲区
start.set_node(start.node + 1); // 调整start状态,使指向
start.cur = start.first; // 下一个缓冲区的第一个元素
}
iterator insert(iterator position, const value_type& value = value_type()) {
if (position == start) {
push_front(value);
return start;
} else if (position == finish) {
push_front(value);
return finish;
} else {
return insert_aux(position, value);
}
}
iterator erase(iterator pos) {
iterator next = pos;
++next;
// 清除点之前的元素个数
difference_type index = pos - start;
// 如果清除点之前的元素个数比较少, 哪部分少就移动哪部分
if (index < (size() >> 1)) {
// 就移动清除点之前的元素
copy_backward(start, pos, next);
pop_front(); // 移动完毕,最前一个元素冗余,去除之
} else // 如果清除点之后的元素个数比较少
{
copy(next, finish, pos); // 就移动清除点之后的元素
pop_back(); // 移动完毕,最后一个元素冗余,去除之
}
return start + index;
}
iterator erase(iterator first, iterator last) {
if (first == start && last == finish) // 如果清除区间是整个deque
{
clear(); // 直接调用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; // 标记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;
}
}
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;
}
void clear() {
// 以下针对头尾以外的每一个缓冲区
for (map_pointer node = start.node + 1; node < finish.node; ++node) {
// 将缓冲区内的所有元素析构
for (int i = 0; i != buffer_size() + 1; i++) {
data_allocator.destroy(*node + i);
}
// 释放缓冲区内存
data_allocator.deallocate(*node, buffer_size());
}
if (start.node != finish.node) // 至少有头尾两个缓冲区
{
for (auto iter = start.cur; iter != start.last; iter++) {
data_allocator.destroy(iter);
}
for (auto iter = finish.first; iter != finish.cur; iter++) {
data_allocator.destroy(iter);
}
// 以下释放尾缓冲区。注意:头缓冲区保留
data_allocator.deallocate(finish.first, buffer_size());
} else // 只有一个缓冲区
for (auto iter = start.cur; iter != finish.cur; iter++) {
data_allocator.destroy(iter); // 将此唯一缓冲区内的所有元素析构
}
// 注意:并不释放缓冲区空间,这唯一的缓冲区将保留
finish = start; // 调整状态
}
};
#endif /* Deque_h */
标签:
原文地址:http://blog.csdn.net/stary_yan/article/details/51601523