stl_deque.h源码剖析

deque概述<stl源码剖析>
  • vector是单向开口的连续线性空间,deque则是双向开口的连续线性空间。双向开口,意思是可以在头尾两端分别做元素的插入和删除操作。
  • deque和vector最大的差异,在于deque允许于常数时间内对头端进行元素的插入或移除操作,二在于deque没有所谓的容量概念,因为它是动态的以分段连续的空间组合而成,随时可以增加一段新的空间并链接起来
  • 像vector那样"因旧空间不足而重新配置一块更大空间,然后复制元素,在释放旧空间",这样的事情在deque是不会发生的
  • 因为deque的迭代器为了提供 random access iterator,相对vector很复杂。因此,除非必要,我们应尽可能的选择vector而非deque,对deque进行的排序工作,为了最高效率,可将deque先完整复制到一个vector身上,对vector排序后(STL sort算法),再复制回deque。
示意图

在这里插入图片描述

__deque_iterator
//如果sizeof(T) 元素大小小于512,则buf_size 为 512 / sz
//否则buf_size 为 1,每个buf保存一个元素.
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>
struct __deque_iterator { 	
	typedef __deque_iterator<T, T&, T*>             iterator;
	typedef __deque_iterator<T, const T&, const T*> const_iterator;
	static size_t buffer_size() { return __deque_buf_size(0, sizeof(T)); }
#endif

	// iterator,五个型别
	typedef random_access_iterator_tag iterator_category; // (1)随机访问
	typedef T value_type; 				// (2)数据类型
	typedef Ptr pointer; 				// (3)指针
	typedef Ref reference; 				// (4)引用
	typedef size_t size_type;
	typedef ptrdiff_t difference_type; 	// (5)
	typedef T** map_pointer;		    // 节点 二级指针 控制中心是指针数组,所以之类是二级指针.

	typedef __deque_iterator self;		

	// 保持与容器的联系
	// cur肯定是在 first 和 last之间
	T* cur;		// 此迭代器所指之buf中的(current)元素
	T* first;	// 此迭代器所指之buf的头
	T* last;	// 此迭代器所指之buf的尾(含备用空间)
	map_pointer node; //指向控制中心的节点
	
	//设置node
	void set_node(map_pointer new_node) {
		node = new_node;
		first = *new_node;	//头结点
		last = first + difference_type(buffer_size());//尾节点(前闭后开)
	}
	//重载相关操作符
	//重载* 解引用
	reference operator*() const { return *cur; }//迭代器中的元素
	//重载前++	
	self& operator++() {
		++cur;					// 切换到下个元素
		if (cur == last) {		// 如果已经到了尾端
			set_node(node + 1);	// node切换到下个缓冲器
			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指向前一个节点的last
		}
		--cur;				// last--才是最后一个元素
		return *this;
	}
	//random_acess 需要支持+=
	//重载+=
	self& operator+=(difference_type n) {
		//cur距 first的距离 + 要偏移的大小
		//也就是要距离first要偏移的位置,
		difference_type offset = n + (cur - first);	
		//如果是负的说明不是在同个buf缓冲区
		//如果>0,但是小于buf_size()说明也在同个buf缓冲区
		if (offset >= 0 && offset < difference_type(buffer_size()))
			cur += n;
		else {
			// 说明目标位置不在同个buf缓冲区
			//要偏移的节点数量 node_offset * buff_size
			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; }
	//重载-,调-=,还是调了+=
	self operator-(difference_type n) const {
		self tmp = *this;
		return tmp -= n; // 喚起operator-=
	}
}
容器deque
template <class T, class Alloc = alloc, size_t BufSiz = 0>
class deque {
public:                         // Basic types
	typedef T value_type;
	typedef value_type* pointer;
	typedef const value_type* const_pointer;
	typedef value_type& reference;
	typedef const value_type& const_reference;
	typedef size_t size_type;
	typedef ptrdiff_t difference_type;

public:                         // Iterators
#ifndef __STL_NON_TYPE_TMPL_PARAM_BUG
	typedef __deque_iterator<T, T&, T*, BufSiz>              iterator;
	typedef __deque_iterator<T, const T&, const T&, BufSiz>  const_iterator;
#else /* __STL_NON_TYPE_TMPL_PARAM_BUG */
	typedef __deque_iterator<T, T&, T*>                      iterator;
	typedef __deque_iterator<T, const T&, const T*>          const_iterator;
#endif /* __STL_NON_TYPE_TMPL_PARAM_BUG */


protected:                      
	// 二级指针
	typedef pointer* map_pointer;
	// (节点)元素配置器,默认每次分配一个元素大小
	typedef simple_alloc<value_type, Alloc> data_allocator;
	// map控制中心配置器,默认每次分配一个指针大小
	typedef simple_alloc<pointer, Alloc> map_allocator;

	static size_type buffer_size() {
		return __deque_buf_size(BufSiz, sizeof(value_type));
	}
	//初始vector大小为8
	static size_type initial_map_size() { return 8; }

protected:    
	//data member                  
	iterator start;		// 头结点
	iterator finish;	// 尾节点
	// vector内每个元素都是一个指针,指向一块连续空间buf
	map_pointer map;	// vector连续空间
	size_type map_size;	// vector大小
public:
	//容器的默认构造函数
	deque()
		: start(), finish(), map(0), map_size(0)
		// 以上 start() 和 finish() 喚起 iterator(亦即 __deque_iterator)
		// 的 default ctor,於是令其 cur, first, last, node 皆為0。
	{
		create_map_and_nodes(0);
	}
	//初始化map_size大小
	static size_type initial_map_size() { return 8; }
	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個,最多是 “所需節點數加2”
		map_size = max(initial_map_size(), num_nodes + 2);
		//配置map_size 控制中心大小
		map = map_allocator::allocate(map_size);

		// 调整nstart,finish到中间位置,初始化时指向同一个节点
		map_pointer nstart = map + (map_size - num_nodes) / 2;
		map_pointer nfinish = nstart + num_nodes - 1;

		map_pointer cur;
		__STL_TRY{
			//节点配置器,在ntart处分配节点空间,默认初始化时配置一个节点
			//并将nstart.cur指向分配出来的缓冲区
			for (cur = nstart; cur <= nfinish; ++cur)
			*cur = allocate_node();
		}
		catch (...) {
			// "commit or rollback" //回滚
			for (map_pointer n = nstart; n < cur; ++n)
				deallocate_node(*n); //回滚释放分配节点内存
			map_allocator::deallocate(map, map_size);
			throw;
		}
		// 设置start和finish的,默认初始化时相同
		// 默认初始化时,执行同个位置,指向出来的buffer缓冲区头
		start.set_node(nstart);
		finish.set_node(nfinish);
		start.cur = start.first;		
		finish.cur = finish.first + num_elements % buffer_size();
	}

public:
	iterator begin() { return start; }	//返回第一个迭代器
	iterator end() { return finish; }	//返回最后一个迭代器的下一个
	//头节点中的元素
	//迭代器重载了* 取出元素值
	reference front() { return *start; } // 喚起 __deque_iterator<>::operator*
	//尾元素
	reference back() {
		iterator tmp = finish;
		--tmp;	// end标识尾元素的下一个,所以--
		return *tmp; 
	}
	//申请buffer_size个元素大小的空间
	pointer allocate_node() { return data_allocator::allocate(buffer_size()); }
	
	//只有在剩一个元素大小的缓冲区时,才调用
	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(vector),vector扩充两倍扩充,搬移
		*(finish.node + 1) = allocate_node();	// 配置器申请一个新缓冲区
		__STL_TRY{
			construct(finish.cur, t_copy);		// 在剩下的那个唯一缓冲区上构造元素
			finish.set_node(finish.node + 1);	// 构造完后才set新的finish节点
			finish.cur = finish.first;			// 调整cur指向
		}
		__STL_UNWIND(deallocate_node(*(finish.node + 1))); //构造元素时抛了异常就还原内存
	}
	//在尾端添加元素
	void push_back(const value_type& t) {
		if (finish.cur != finish.last - 1) {
			// 最后一个缓冲区还有位置
			construct(finish.cur, t);	// 在备用空间上构造元素 place new
			++finish.cur;	// 调整尾节点的cur值
		}
		else  // 还有一个元素大小的缓冲区时,申请内存
			push_back_aux(t);
	}

	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;
		//节点数量的2倍小于map大小时,不扩充,调整位置
		//节点数量的2倍不小于map大小时,扩充map
		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;
			// 配置一块新的内存(new_map_size个T指针的大小(任何类型的指针大小一样))
			map_pointer new_map = map_allocator::allocate(new_map_size);
			//找新的start位置(考虑如果是往前加节点,就考虑一下添加节点的数量)
			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.set_node(new_nstart);
		finish.set_node(new_nstart + old_num_nodes - 1);
	}
	//在尾节点扩充时
	void reserve_map_at_back(size_type nodes_to_add = 1) {
		//前闭后开 =  1时已经是没有了,所以是小于2
		if (nodes_to_add + 1 > map_size - (finish.node - map))
			// 如果 map 尾端的节点空间不足
			// 此时需要扩充map,配置更大的Map,拷贝原来的,释放原来的
			// false标识调整map上node的位置时不考虑新增加的节点数量
			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。也就是vector的头地址.
			// 此时需要扩充map,配置更大的Map,拷贝原来的,释放原来的
			//true标识,调整map上node的位置时,为新增加的节点在map前部预留出位置
			reallocate_map(nodes_to_add, true);
	}
	// 当第一个缓冲区没有备用空间时调用
	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();		//  符合某种条件时调整map(vector),vector扩充两倍扩充,搬移
		*(start.node - 1) = allocate_node();	// 配置一个新节点
		__STL_TRY{
			start.set_node(start.node - 1);		// 改变start节点指向
			start.cur = start.last - 1;			// 调整start.cur,因为前闭后开,所以cur指向start.last的上一个
			construct(start.cur, t_copy);		// 在start.cur构造元素  place new
		}
		catch (...) {
			// "commit or rollback 
			start.set_node(start.node + 1);
			start.cur = start.first;
			deallocate_node(*(start.node - 1));
			throw;
		}
	}
	//在头端添加元素
	void push_front(const value_type& t) {
		if (start.cur != start.first) { 	// 因为前闭后开所以跟last逻辑不一样
			construct(start.cur - 1, t); 	// 在前面构造 place new
			--start.cur;		// 调整头结点的cur
		}
		else // 第一个缓冲区没有备用缓冲了
			push_front_aux(t);
	}

	
	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;						//front标记第二个元素(也就是原来的第一个元素,与前端插入的那个元素相同)
			iterator front2 = front1;
			++front2;						//原来的第二个元素,也就是目前第三个元素
			pos = start + index;			//插入点前的最后一个元素
			iterator pos1 = pos;
			++pos1;							//前闭后开,所以++
			copy(front2, pos1, front1);		// 元素搬移(从[front,pos1)赋值到front开始的地方)
		}
		else {						// 安插点之后的元素较少
			push_back(back());			// 在最尾端加入与最后一个元素相同的元素
			iterator back1 = finish;	
			--back1;					//最后一个元素
			iterator back2 = back1;
			--back2;					//倒数第二个元素,与最后一个元素相同
			pos = start + index;
			// 元素搬移(前闭后开[pos,back2),逆序复制,back1是逆序复制元素的结尾)
			// 也就是back2的上一个,是back1的上一个元素
			// 覆盖了倒数第二个元素,也就是覆盖掉了back2
			copy_backward(pos, back2, back1);	
		}
		*pos = x_copy;	// 元素已经搬移了,在pos位置处安插新元素
		return pos;
	}
	// 在position处安插一个元素,其值为x
	iterator insert(iterator position, const value_type& x) {
		if (position.cur == start.cur) {	// 如果安插点是deque的最前端
			push_front(x);				    // push_front去做
			return start;
		}
		else if (position.cur == finish.cur) { // 如果安插点是deque的最尾端
			push_back(x);					  //  交给push_front去做
			iterator tmp = finish;
			--tmp;
			return tmp;
		}
		else {
			return insert_aux(position, x);	 	// 交給 insert_aux 去做
		}
	}
	//删除元素
	iterator erase(iterator pos) {
		iterator next = pos;
		++next;
		difference_type index = pos - start;	// 清除点之前的元素个数
		if (index < (size() >> 1)) {			// 如果清除点之前的元素比较少,
			copy_backward(start, pos, next);	// 把前面的元素搬到pos位置上来,覆盖掉pos位置上的元素
			pop_front();				// 搬移完毕,删除第一个元素
		}
		else {					// 清除点之后的元素个数比较少
			copy(next, finish, pos);	// 从Pos下一个之后的元素搬到Pos位置处,覆盖pos。
			pop_back();				// 搬移完毕,把最后一个元素删掉
		}
		return start + index;		//返回一个迭代器
	}

	iterator erase(iterator first, iterator last);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值