「STL详解」顺序容器 deque 详解

1. deque 概述

vector 是单向开口的连续线性空间,deque 是一种双向开口的连续线性空间,即可以在头尾两端做元素的插入和删除操作。

deque 和 vector 的区别:

  1. deque 可以在常数时间内对头端进行元素的插入和移除操作;
  2. deque 没有容量(capacity)的概念,它是动态地分段连续空间组合而成,随时可以增加一段新空间并连接起来。即 vector 是在旧空间不足时开辟新空间,然后将数据拷贝过来再释放原来的空间;而 deque 则不需要提供空间保留功能。

deque 的迭代器不是普通指针,底层实现复杂,因此应尽量选择使用 vector 而不是 deque。deque 的排序操作是将 deque 完整复制到一个 vector 上,然后通过 vector 排序,再复制到 deque。

2. 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:
	// 元素的指针的指针 map
	typedef pointer*	map_pointer;
protected:
	// 数据成员
	map_pointer map;		// 指向 map,map 是一块连续空间
							// 每个元素都是一个指向缓冲区的指针
	size_type map_size;		// map 内可以容纳多个指针
};

注意:map 就是就是一个 T**

3. deque 的迭代器

为了维护“整体连续”,deque 的迭代器主要任务是设计 operator++operator--

deque 的迭代器必须能够指出缓冲区在哪里,能够判断自己是否已经处于当前缓冲区的边缘,如果是,则前进或后退时就必须跳跃到下一个或上一个缓冲区。为了正确跳跃,deque 必须随时掌握管控中心 map。

deque 的中控器、缓冲区、迭代器的相互关系:


template<class T, class Ref, class Ptr, size_t BufSiz>
struct __deque_iterator {		// 未继承std::iterator
	typedef __deque_iterator<T, T&, T*, const 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	different_type;		// 用来表示两个迭代器之间的距离
	typedef T**			map_pointer;
	
	typedef __deque_iterator self;
	
	// 保持与容器的联结
	T* cur;		// 迭代器所指缓冲区的现行元素
	T* first;	// 迭代器所指缓冲区的头
	T* last;	// 迭代器所指缓冲区的尾
	map_pointer node;	// 指向中控器 map
};

其中的 buffer_size() 函数调用 __deque_buf_size() 全局函数,定义如下:

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));
}

如果 n 不为 0 则传回 n,表示 buffer size 由用户自定义;如果 n 为 0,表示 buffer size 使用默认值,即如果元素大小小于 512,传回 512/sz,如果元素大小不小于 512,传回 1。

deque::begin() 传回迭代器 start,deque::end() 传回迭代器 finish。这两个迭代器都是deque 的 data member。对 deque 迭代器内各种指针运算要注意一旦进行时遇到缓冲区边缘,可能需要调用 set_node() 跳到下一个缓冲区:

void set_node(map_pointer new_node) {
	node = new_node;
	first = *new_node;
	last = first + difference_type(buffer_size());
}

迭代器重载运算子:

template<class T, class Ref, class Ptr, size_t BufSiz>
struct __deque_iterator {
	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+= 来完成 operator-=
	self& operator-=(difference_type n)	{ return *this += -n; }
	
	self operator-(difference_type n) const {
		self tmp = *this;
		return tmp -= n;
	}
	// 调用 operator* 和 operator+ 来实现随机存取
	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 的数据结构

deque 除了维护指向 map 的指针,也维护 start,finish 两个迭代器,分别指向第一缓冲区的第一个元素和最后缓冲区的最后一个元素的下一个位置。此外还必须记住目前 map 的大小。

// 缺省使用 alloc 为配置器
template<class T, class Alloc = alloc, size_t BufSiz = 0>
class deque {
public:
	typedef T value_type;
	typedef value_type* pointer;
	typedef size_t size_type;
public:		// 迭代器
	typedef __deque_iterator<T, T&, T*, BufSiz>	iterator;
protected:
	// 元素的指针的指针
	typedef pointer* map_pointer;
protected:
	iterator start;		// 第一缓冲区第一个元素
	iterator finish;	// 最后缓冲区最后一个元素的下一个位置
	map_pointer map;	// 指向 map,map 每个元素都是指针,指向一个节点(缓冲区)
	size_type map_size;
public:
	// deque 的使用机能
	iterator begin()	{ return start; }
	iterator end()		{ return finish; }
	
	reference operator[](size_type n) {
		// 调用 __deque_iterator<>::operator[]
		return start[difference_type(n)];
	}
	// 调用 __deque_iterator<>::operator*
	reference front()	{return *start; }
	reference back() {
		// __deque_iterator<> 没有为 (finish - 1) 定义运算子
		// 因此下面三行不能修改为 return *(finish - 1)
		iterator tmp = finish;
		--tmp;		// 调用 __deque_iterator<>::operator--
		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 的构造与内存管理

声明一个 deque:

deque<int, alloc, 32> ideq(20, 9);

缓冲区大小设置为 32bytes,令其保留 20 个元素空间,每个元素初值为 9。为了指定第三个参数缓冲区大小,必须将前两个参数都指明出来,因此必须明确指定 alloc 为空间配置器。

deque 自行定义了两个专属的空间配置器:

// 每次配置一个元素大小
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<classT, 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);
			// 尾端可能有备用空间,所以最后一个节点设定有点不同,不用设定初值
			uninitialize_fill(finish.first, finish.cur, value);
		}
		catch(...) {
			...
		}
}
// creat_map_and_nodes 负责生产并安排好 deque 的结构
templat<class T, class Alloc, size_t BufSize>
void deque<T, Alloc, BufSize>::creat_map_and_nodes(size_type num_elements)
{
	// 需要的节点数=(元素个数/每个缓冲区可容纳元素个数)+1
	// 如果刚好整除,会多配一个节点
	size_type num_nodes = num_elements / buffer_size() + 1;
	// 一个 map 要管理几个节点,最少 8 个,最多是 所需节点数+2
	// 前后各留一个,扩充时可用
	map_size = max(initial_map_size(), num_nodes + 2);
	map = map_allocator::allocate(map_size);
	// 以上配置出具有 map_size 个节点的 map
	
	// 令 nstart 和 nfinish 指向 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();
	}
	catch(...) {
		// 如果有一个不成功,就全部删除
	}
	
	// 为 deque 内的两个迭代器 start 和 end 设定正确内容
	start.set_node(nstart);
	finish.set_node(nfinish);
	start.cur = start.first;
	finish.cur = finish.first + num_elements % buffer_size();
}

6. deque 的元素操作

push_back() 和 push_front()

push_back() 增加一个新元素于尾端:

void push_back(const value_type& t) {
	if (finish.cur != finish.last - 1) {
		// 缓冲区还有一个以上的备用空间
		construct(finish.cur, t);	// 直接在备用空间上构造元素
		++finish.cur;				// 调整最后缓冲区的使用状态;
	}
	else		// 缓冲区没有或只剩一个元素备用空间
		push_back_aux(t);
}
/* push_back_aux 先配置一整块新的缓冲区,在设置新元素内容,
 * 最后更改迭代器 finish 状态
 * 当最后一个缓冲区只剩一个备用元素空间才被调用 */
template<class T, class Alloc, size_t BufSize>
void deque<T, Alloc, BufSize>::push_bcak_aux(const value_type& t) {
	value_type t_copy = t;
	reverse_map_at_back();		// 符合某种条件则必须重换一个 map
	*(finish.node + 1) = allocate_node();	// 配置一个新节点(缓冲区)
	__STL_TRY {
		construct(finish.cur, t_copy);
		finish.set_node(finish.node + 1);
		finish.cur = finish.first;
	}
	__STL_UNWIND(deallocate_node(*(finish.node + 1)));
}

push_front() 将一个新元素插入 deque 的前端:

void push_front(const value_type& t) {
	if (start.cur != start.first) {		// 第一缓冲区有备用空间
		construct(start.cur - 1, t);	// 直接在备用空间上构造元素
		--start.cur;					// 调整第一缓冲区的使用状态
	}
	else		// 第一缓冲区无备用空间
	push_front_aus(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;
	reverse_map_at_front();
	*(start.node - 1) = allocate_node();	// 配置一个新节点(缓冲区)
	__STL_TRY {
		start.set_node(start.node - 1);		// 改变 start,令其指向新节点
		start.cur = start.last - 1;			// 设定 start 的状态
		construct(start.cur, t_copy);		// 针对标的元素设值
	}
	catch(...) {
		// 若不是全部成功,就一个不留
		start.set_node(start.node + 1);
		start.cur = start.first;
		deallocate_node(*(start.node - 1));
		throw;
	}
}

deque 容器的 map 什么时候需要重新整治,由 reserve_map_at_back()reserve_map_at_front() 进行判断,实际操作则由 reallocate_map() 执行:

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);
}

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 使用
		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;
		mao_size = new_map_size;
	}
	// 重新设定迭代器 start 和 finish
	start.set_node(new_nstart);
	finish.set_node(new_nstart + old_num_nodes - 1);
}

pop_back()、pop_front()

无论是在 deque 的最前端还是最尾端取元素,都需要考虑在某种条件下将缓冲区释放掉:

void pop_back() {
	if (finish.cur != finish.frist) {
		// 最后的缓冲区至少有一个元素
		--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_front() 取出第一个元素:

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;
}

clear()、erase()

clear() 用来清除整个 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()
		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());
	}
	else	// 只有一个缓冲区
		destroy(start.cur, finish.cur);		// 将此缓冲区元素析构
	finish = start;	// 调整状态
}

erase() 用来清除某个元素:

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;
}

erase() 用来清除 [first, last) 内的所有元素:

template<class T, class Alloc, size_t BufSize>
deque<T, Alloc, BufSize>::iterator
deque<T, Alloc, BufSize>::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)
				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;
	}
}

insert()

deque 为 insert() 功能提供了多个版本,最基础最重要的是以下版本,允许在某个点(之前)插入一个元素,并设定其值:

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 {
		return insert_aux(position, x);
	}
}
template<class T, class Alloc, size_t BufSize>
typename 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;
		iteraotr pos1 = pos;
		++pos1;
		copy(front2, pos1, front1);		// 元素移动
	}
	else {		// 插入点之后元素比较少
		push_back(back());				// 在尾端加入与最后元素同值元素
		iterator back1 = finish;
		--back;
		iterator back2 = back1;
		--back;
		pos = start + index;
		copy_backward(pos, back2, back1);
	}
	*pos = x_copy;		// 在插入点设定新值
	return pos;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值