STL deque 源码——deque特点、实现框架、源码分段剖析、常用函数总结(下)之源码剖析

源码剖析

首先定义每个缓冲区的长度:

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

__deque_iterator的数据结构

template <class T, class Ref, class Ptr, size_t BufSiz>
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)); }
 
	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;       // 此迭代器所指之缓冲区中的现行元素
	T* first;     // 此迭代器所指之缓冲区的头
	T* last;      // 此迭代器所指之缓冲区的尾(含备用空间)
	map_pointer node;    // 指向管控中心

迭代器的构造函数

	__deque_iterator(T* x, map_pointer y)
		: cur(x), first(*y), last(*y + buffer_size()), node(y) {}
	__deque_iterator() : cur(0), first(0), last(0), node(0) {}
	__deque_iterator(const iterator& x)
		: cur(x.cur), first(x.first), last(x.last), node(x.node) {}
 

下面重载的这些运算符是让deque从外界看上去维护的是一段连续空间的关键!!

①前缀自增
如果当前迭代器指向元素是当前缓冲区的最后一个元素则将迭代器状态调,整为下一个缓冲区的第一个元素, 不是当前缓冲区最后一个元素的话执行前缀自增前的状态

	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个元素, n可以为负
先判断会不会超出(往前或往后)当前缓冲区,如果不会的话就直接加cur+n
如果会超出的话,由于可以是负数,所以要考虑是往前移动多少个缓冲区或者往后移动多少个缓冲区,之后就是调整缓冲区,调整cur指针

// 迭代器可以直接跳跃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指针至正确的元素
			cur = first + (offset - node_offset * difference_type(buffer_size()));
		}
		return *this;
	}

⑤其他一些运算符重载

	reference operator*() const { return *cur; }
 
	// 判断两个迭代器间的距离
	difference_type operator-(const self& x) const
	{
		return difference_type(buffer_size()) * (node - x.node - 1) +
			(cur - first) + (x.last - x.cur);
	}
	
//在+=基础上的const型
	self operator+(difference_type n) const
	{
		self tmp = *this;
 
		// 这里调用了operator +=()可以自动调整指针状态
		return tmp += n;
	}
//-n就是+n,只不过传入-n
self& operator-=(difference_type n) { return *this += -n; }
 
	self operator-(difference_type n) const
	{
		self tmp = *this;
		return tmp -= 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);
	}

⑥map新增节点

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

deque的数据结构

template <class T, class Alloc = alloc, size_t BufSiz = 0>
class deque
{
...
};
一系列基础:

公有属性、迭代器别名、指向管理中心map指针、分配器、获取缓冲区最大存储元素数量,初始化map大小

public:                         // Basic types
	typedef T value_type;
	typedef value_type* pointer;
	typedef value_type& reference;
	typedef size_t size_type;
	typedef ptrdiff_t difference_type;
 
public:                         // Iterators
	typedef __deque_iterator<T, T&, T*, BufSiz>       iterator;
 
protected:                      // Internal typedefs
 
	typedef pointer* map_pointer;
 
	// 这个提供STL标准的allocator接口, 见<stl_alloc.h>
	typedef simple_alloc<value_type, Alloc> data_allocator;
	typedef simple_alloc<pointer, Alloc> map_allocator;
 
	// 获取缓冲区最大存储元素数量
	static size_type buffer_size()
	{
		return __deque_buf_size(BufSiz, sizeof(value_type));
	}
 
	static size_type initial_map_size() { return 8; }
 
重要的四个成员变量四个指针

连接缓冲区与map, map是一个连续的空间,每一个元素都是一个指针,指针指向一个缓冲区

protected:                      // Data members
	iterator start;               // 起始缓冲区
	iterator finish;              // 最后一个缓冲区
	map_pointer map;
	size_type map_size;   // map容量

简单的基础函数

	iterator begin() { return start; }
	iterator end() { return finish; }
 
	// 重载operator []  提供随机访问能力
	reference operator[](size_type n) { return start[difference_type(n)]; }
 
	reference front() { return *start; }//返回reference 
	reference back()
	{
		iterator tmp = finish;
		--tmp;//前闭后开
		return *tmp;
	}
 
	// 当前容器拥有的元素个数, 调用迭代器重载的operator -
	size_type size() const { return finish - start;; }
	size_type max_size() const { return size_type(-1); }
 
	// deque为空的时, 只有一个缓冲区
	bool empty() const { return finish == start; }
 
 //缓冲区中是first和last
构造函数和析构函数,

注意拷贝构造函数,如果当前容器size大,将多出来的erase掉, 用法erase(copy(x.begin(), x.end(), start), finish);先将x的元素先拷贝过去,copy函数返回的是一个迭代器指出已被赋值元素区间的最后一个位置,然后就在原容器中从这个copy函数返回位置到最后元素这个区间删除,即多出来的范围删除用。法就很好
如果当前容器长度不够的话,注意deque容器会将所有x超出的元素用insert追加进去,duque的insert函数后面再看

public:                         // Constructor, destructor.
	deque() : start(), finish(), map(0), map_size(0)
	{
		create_map_and_nodes(0);//就是创建map和node
	}
 
	deque(size_type n, const value_type& value)
		: start(), finish(), map(0), map_size(0)
	{
		fill_initialize(n, value);
	}
 
	deque(int n, const value_type& value)
		: start(), finish(), map(0), map_size(0)
	{
		fill_initialize(n, value);
	}
 
 
	~deque()
	{
		destroy(start, finish);     // <stl_construct.h>
		destroy_map_and_nodes();
	}
 
	deque& operator= (const deque& x)
	{
		// 拷贝构造函数,如果当前容器size大,将多出来的erase掉, 用法erase(copy(x.begin(), x.end(), start), finish);先将x的元素先拷贝过去,copy函数返回的是一个迭代器指出已被赋值元素区间的最后一个位置,然后就在原容器中从这个位置到最后元素这个区间的删除,即多出来的范围删除,用法就很好
		// 如果当前容器长度不够的话,注意deque容器会将所有x超出的元素用insert追加进去,duque的insert函数后面再看
		
		const size_type len = size();
		if (&x != this)
		{
			// 当前容器比x容器拥有元素多, 析构多余元素
			if (len >= x.size())
				erase(copy(x.begin(), x.end(), start), finish);
			// 将x所有超出部分的元素使用insert()追加进去
			else {
				const_iterator mid = x.begin() + difference_type(len);
				copy(x.begin(), mid, start);
				insert(finish, mid, x.end());
			}
		}
		return *this;
	}
 
正经的牵扯到map的内存, 跨区块的函数们

push / pop函数, 先不考虑容器满或空的情况

public:
	void push_back(const value_type& t)
	{
		// 最后缓冲区尚有两个(含)以上的元素备用空间
		if (finish.cur != finish.last - 1)
		{
			construct(finish.cur, t);     // 直接在备用空间上构造元素
			++finish.cur;     // 调整最后缓冲区的使用状态
		}
		// 容量已满就要新申请内存了
		else
			push_back_aux(t);
	}
 
	void push_front(const value_type& t)
	{
		if (start.cur != start.first)      // 第一缓冲区尚有备用空间
		{
			construct(start.cur - 1, t);   // 直接在备用空间上构造元素
			--start.cur;     // 调整第一缓冲区的使用状态
		}
		else    // 第一缓冲区已无备用空间
			push_front_aux(t);
	}
 
	void pop_back()
	{
		if (finish.cur != finish.first)    // 最后缓冲区有一个(或更多)元素
		{
			--finish.cur;    // 调整指针,相当于排除了最后元素
			destroy(finish.cur);    // 将最后元素析构
		}
		else
			// 最后缓冲区没有任何元素
			pop_back_aux();    // 这里将进行缓冲区的释放工作
	}
 
	void pop_front()
	{
		if (start.cur != start.last - 1)    // 第一缓冲区有两个(或更多)元素
		{
			destroy(start.cur);    // 将第一元素析构
			++start.cur;           //调整指针,相当于排除了第一元素
		}
		else
			// 第一缓冲区仅有一个元素
			pop_front_aux();    // 这里将进行缓冲区的释放工作
	}

insert函数,重点就是insert_aux函数

insert_aux在指定位置pos插入元素,需要判断是pos前端元素少还是后端少前:端少的话首先复制当前front值push到最前端,将元素往前拷贝移动;同理后端元素少的话,先复制一个最后元素push到最尾端,再拷贝后移;最后在位置pos上设定新值即可

iterator insert(iterator position, const value_type& x)
	{
		// 如果是在deque的最前端插入, 那么直接push_front()即可
		if (position.cur == start.cur)
		{
			push_front(x);
			return start;
		}
		// 如果是在deque的末尾插入, 直接调用push_back()
		else if (position.cur == finish.cur)
		{
			push_back(x);
			iterator tmp = finish;
			--tmp;
			return tmp;
		}
		else
		{
			return insert_aux(position, x);
		}
	}
	
//`insert_aux`
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;
}

erase函数、清除某个位置的元素、清除某个区间的元素

删除某个值的元素:删除完元素之后要移动元素,所以依旧要先判断是前端移动的元素少还是后端,代码中右移1位就是除以2
如果是前端的元素少,复制拷贝pos前的元素,最后再将重复的第一个元素删除;如果是后端的元素少,就移动之后的元素,最后将重复的最后一个元素删除;返回删除位置的元素

清除一个区间[ )的元素:如果清除区间是整个deque的话,直接调用clear()即可;否则
首先记录要删除区间的长度,以及前端待移动的元素用于判断是移动前端元素好还是后端元素好:如果是前方元素较少,将前端元素拷贝复制向后移动以覆盖待清除的区间,标记新起点new_start = start + n, 删除前端重复的元素并释放其空间,更新起点start = new_start;如果后端元素较少,则向前拷贝移动元素,标记新finish点,析构并释放重复的元素,最后更新finish为标记的finish

//清除某个位置的元素
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;
	}

//清除某个区间的元素
deque<T, Alloc, BufSize>::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;
	}
}

下面解析push/pop超出当前缓存区时的情况:

    void push_back_aux(const value_type& t);
	void push_front_aux(const value_type& t);
	void pop_back_aux();
	void pop_front_aux();

👇
push_back_aux:先调用reverse_map_at_back,若符合某种条件,重换一个map;分配空间。
reserve_map_at_back:看看map有没有满,满的话,调用reallocate_map。
reallocate_map:如果前端或后端pop过多,就会导致大量的空闲空间,如果是这种情况,则不用新分配空间,调整一下start的位置即可
总之,reserve_map_at_back就已经把map给搞定了,

因此

push_back_aux的情况,给新配置一个缓冲区(node),记得前闭后开的原则,给当前缓冲区的cur指针赋值,调整finish这个缓冲区的状态,finish.cur = finish.first
pop_back_aux:释放最后一个缓冲区调,整finish状态,使指向上一个缓冲区的最后一个元素,将该元素析构
push_front_auxpop_front_aux同理

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();
	*(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)));
}
 
// Called only if 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();
	__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;
	}
}
 
// 只有当 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);                // 将该元素析构
}
 
// 只有当 start.cur == start.last - 1 时才会被调用
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状态,使指向
	start.cur = start.first;         // 下一个缓冲区的第一个元素
}
# 高校智慧校园解决方案摘要 智慧校园解决方案是针对高校信息化建设的核心工程,旨在通过物联网技术实现数字化校园的智能化升级。该方案通过融合计算机技术、网络通信技术、数据库技术和IC卡识别技术,初步实现了校园一卡通系统,进而通过人脸识别技术实现了更精准的校园安全管理、生活管理、教务管理和资源管理。 方案包括多个管理系统:智慧校园管理平台、一卡通卡务管理系统、一卡通人脸库管理平台、智能人脸识别消费管理系统、疫情防控管理系统、人脸识别无感识别管理系统、会议签到管理系统、人脸识别通道管理系统和图书馆对接管理系统。这些系统共同构成了智慧校园的信息化基础,通过统一数据库和操作平台,实现了数据共享和信息一致性。 智能人脸识别消费管理系统通过人脸识别终端,在无需接触的情况下快速完成消费支付过程,提升了校园服务效率。疫情防控管理系统利用热成像测温技术、视频智能分析等手段,实现了对校园人员体温监测和疫情信息实时上报,提高了校园公共卫生事件的预防和控制能力。 会议签到管理系统和人脸识别通道管理系统均基于人脸识别技术,实现了会议的快速签到和图书馆等场所的高效通行管理。与图书馆对接管理系统实现了一卡通系统与图书馆管理系统的无缝集成,提升了图书借阅的便捷性。 总体而言,该智慧校园解决方案通过集成的信息化管理系统,提升了校园管理的智能化水平,优化了校园生活体验,增强了校园安全,并提高了教学和科研的效率。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值