STL源码剖析:3.序列式容器(下)


1.deque容器

deque容器是一种双向开口的连续线性空间,双向开口意为可以在头尾两端分别做元素的插入和删除操作。deque没有容量的概念,因为它是动态地以连续空间组合而成,随时可以增加一段新的空间并链接起来。

1.1.deque容器的中控器
从逻辑上看来deque是连续空间,deque是由一段一段的定量连续空间组成,一旦有必要在deque的前端或尾端增加新空间,便配置一段定量连续空间,串联在整个deque的头端或尾端,deque的最大任务便是在这些分段的定量连续空间上,维护其整体连续的假象,并提供随机存取的接口。
deque采用一块map(非容器)作为主控,这里的map采用一小块连续空间,并且其中的每个元素都是指针,指向另外一段较大的连续线性空间,成为缓冲区,我们通过这个缓冲区来进行存储,因此由此来看,所谓map,其实就是一个T**。

//BufSize代表的是缓冲区的大小
template<class T,class Alloc = alloc, size_t BufSize = 0>
class deque {
public:
	typedef T value_type;
	typedef value_type* pointer;
	
protected:
	typedef pointer* map_pointer;
	
	map_pointer map;
	size_type map_size;//map中的指针个数
};

1.2 deque的迭代器
本质上来数deque是分段连续空间,为了维持其整体连续假象的任务,我们通过iterator来完成。
首先我们定义一个全局函数来决定缓冲区的大小:

//deque的BufSize
inline size_t deque_buf_size(size_t n, size_t sz) {
	//缓冲区划分:如果传入的想要开辟的大小不为0的话,就使用传入的n作为缓冲区的大小
	//如果传入的想要开辟的大小为0的话,就用要存储的元素与512Bytes比较,如果小于512Bytes,使用512/n作为默认缓冲区
	//大小,否则返回1
	return n != 0 ? n : (sz < 512 ? size_t(512 / sz) : size_t(1));
}

对于deque的迭代器而言,它必须能够指出分段连续空间在哪里,并且在递增或者是递减的时候能够判断出来自己是否已经在缓冲区的边缘位置,如果在的话,根据具体的操作决定自己是跳跃至上一个缓冲区还是下一个缓冲区中,deque_iterator的定义如下所示:

//deque_iterator迭代器
template<class T, class Ref, class Ptr, size_t BufSize>
struct deque_iterator {
	friend template <class T> struct deque_buf_size;

	typedef deque_iterator<T, T&, T*, BufSize> iterator;
	typedef deque_iterator<T, const T&, const T*, BufSize> const_iterator;
	typedef deque_iterator self;
	static size_t buffer_size() { return deque_buf_size(Bufsize); }

	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;//指向中控器

	T* cur;//缓冲区当前元素位置
	T* first;//指向缓冲区的头部位置
	T* last;//此迭代器所指之缓冲区的尾部(含备用空间)
	map_pointer node;//指向中控器
};

以下定义几个关键操作,使得迭代器能够满足我们之前的需求,也就是在缓冲区边缘时进行的操作:
1.2.1 调到目标缓冲区set_node():

template<class T, class Ref, class Ptr, size_t BufSize>
struct deque_iterator {
	void set_node(map_pointer new_node) {
		node = new_node;
		first = *new_node;
		last = first + difference_type(buffer_size());
	}
};

1.2.2 对于迭代器来讲必须要进行重载的*,->操作:

template<class T, class Ref, class Ptr, size_t BufSize>
struct deque_iterator {
	reference operator*() const { return *cur; }
	pointer operator->() const { return &(operator*()); }
};

1.2.3 ++,–操作

template<class T, class Ref, class Ptr, size_t BufSize>
struct deque_iterator {
	self& operator++() {
		++cur;
		if (cur == last) {
			set_node(node + 1);//跳转到下一个缓冲区 并设定其first以及last、node
			cur = first;
		}
		return *this;
	}
	self& operator++(int) {
		self tmp = *this;
		++*this;
		return tmp;
	}
	self& operator--() {
		if (cur == first) {
			set_node(node - 1);//跳转到下一个缓冲区 并设定其first以及last、node
			cur = last;
		}
		--cur;
		return *this;
	}
	self& operator--(int) {
		self tmp = *this;
		--*this;
		return tmp;
	}
};

1.2.4 +,+=,-,-=操作

template<class T, class Ref, class Ptr, size_t BufSize>
struct deque_iterator {
	self& operator+=(difference_type n) {
		difference_type offset = n + (cur - first);
		if (offset >= 0 && offset < difference_type(buffer_size()))
			cur += n;
		else {
			//如果大于一个缓冲区的话,也就意味着跳入其他缓冲区
			//决定前进还是后退,node_offset表示的是步数
			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;
	}
	self& operator-=(difference_type n) { return *this += (-n); }
	self operator-(difference_type n) const {
		self tmp = *this;
		return tmp -= n;
	}
};

1.2.5 []、==、!=、<等操作:

template<class T, class Ref, class Ptr, size_t BufSize>
struct deque_iterator {
	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);
	}
};

1.3 deque的数据结构
deque除了维护一个map以外,还维护start(指向第一缓冲区的第一个元素)和finish(指向最后缓冲区的最后一个元素),并且通过map_size来决定当前的map节点是否充足,是否需要重新配置一块更大的map。

template<class T,class Alloc = alloc, size_t BufSize = 0>
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;

public:
	typedef deque_iterator<T, T&, T*, BufSize> iterator;
protected:
	typedef pointer* map_pointer;

	iterator start;
	iterator finish;
	map_pointer map;
	size_type map_size;//map中的指针个数
};

1.4 deque的构造与内存管理
deque定义了两个专属的空间配置器:

protected:
	//每次配置一个元素
	typedef simple_alloc<value_type, Alloc> data_allocator;
	//每次配置一个指针
	typedef simple_alloc<pointer, Alloc> map_allocator;

1.4.1 deque的形如deque<> deq(n,value)的构造函数。

template<class T,class Alloc = alloc, size_t BufSize = 0>
class deque {
	deque(size_type n, const value_type& value)
	{
		fill_init(n, value);
	}
};

其中调用了fill_init()函数,fill_init()函数如下所示,首先他会配置一个新的map以及创建一个节点,每次对于已经配置的好的缓冲区的使用value进行填充,最后再将最后一个缓冲区填充了。

template<class T, class Alloc, size_t BufSize>
void deque<T, Alloc, BufSize>::fill_init(size_type n, const value_type& value) {
	create_map_and_nodes(n);
	map_pointer cur;
	if (n != 0) {
		for (cur = start.node; cur < finish.node; ++cur)
			ministl::uninitialized_fill(*cur, *cur + buffer_size(), value);
		ministl::uninitialized_fill(finish.node, finish.cur, value);
	}
}

create_map_and_nodes()函数的作用创建一个缓冲区,然后令开始于结束指针都指向map的中心位置,这样子的话map就可以像两头开始扩充,代码如下所示:

template<class T, class Alloc, size_t BufSize>
void deque<T, Alloc, BufSize>::create_map_and_nodes(size_type num_elements) {
	//map需要的节点数 = 元素个数 / 每个缓冲区可容纳的元素个数 + 1
	size_type num_nodes = num_elements / buffer_size() + 1;

	//一个map指针要管理几个结点,最少八个,最多是所需节点数加2 前后各预留一个扩充时使用
	map_size = ministl::max(initial_map_size(), num_nodes + 2);
	try
	{
		map = create_map(map_size);
	}
	catch (...)
	{
		map = nullptr;
		map_size = 0;
		throw;
	}

	//令nstart以及nfinish指向map所拥有全部的节点的最中心的区段。
	map_pointer nstart = map + (map_size - num_nodes) / 2;
	map_pointer nfinish = nstart + num_nodes - 1;
	try
	{
		//为每个节点创建缓冲区
		create_buffer(nstart, nfinish);
	}
	catch (...)
	{
		map_allocator::deallocate(map, map_size);
		map = nullptr;
		map_size = 0;
		throw;
	}
	//这里start指针指向的是map数组的首个指针的首地址,finish指向的则是map数组的最后一个指针的末尾地址
	start.set_node(nstart);
	finish.set_node(nfinish);
	start.cur = start.first;
	finish.cur = finish.first + (num_elements % buffer_size());
}

1.4.2 可以使用某一迭代器范围内的元素来进行初始化形如deque<> deq(iterator,iterator),或者是使用initializer_list来进行初始化:

template<class T,class Alloc = alloc, size_t BufSize = 0>
class deque {
	template <class IIter>
	deque(IIter first, IIter last)
	{
		copy_init(first, last, iterator_category(first));
	}
	deque(std::initializer_list<value_type> ilist)
	{
		copy_init(ilist.begin(), ilist.end(), ministl::forward_iterator_tag());
	}
};

其中我们可以看到,使用了copy_init()函数,该函数的功能是将传入迭代器范围内的元素填充到deque容器中,

template<class T, class Alloc, size_t BufSize>
template <class Iterator>
void deque<T, Alloc, BufSize>::copy_init(Iterator first, Iterator last, input_iterator_tag) {
	const size_type n = ministl::distance(first, last);
	create_map_and_nodes(n);
	for (; first != last; ++first)
		push_back(*first);
}

template<class T, class Alloc, size_t BufSize>
template <class Iterator>
void deque<T, Alloc, BufSize>::copy_init(Iterator first, Iterator last, forward_iterator_tag) {
	const size_type n = ministl::distance(first, last);
	create_map_and_nodes(n);
	for (auto cur = start.node; cur < finish.node; ++cur)
	{
		auto next = first;
		ministl::advance(next, buffer_size);
		ministl::uninitialized_copy(first, next, *cur);
		first = next;
	}
	ministl::uninitialized_copy(first, last, finish.first);
}

1.4.3 拷贝构造函数

template<class T,class Alloc = alloc, size_t BufSize = 0>
class deque {
	deque(const deque& rhs)
	{
		copy_init(rhs.begin(), rhs.end(), ministl::forward_iterator_tag());
	}
};

1.4.4 赋值函数
首先我们要进行大小比较操作,如果当前deque的容器大于传入的deque容器,那么就将当前传入容器大于的部分给去掉,然后在执行拷贝操作,否则的话就先将容器内填满,然后再调用insert()函数进行插入,直到将传入的容器所有元素都插入进去为止。

template<class T,class Alloc = alloc, size_t BufSize = 0>
class deque {
	template <class IIter>
	deque& operator=(const deque& rhs)
	{
	if (this != &rhs)
	{
		const auto len = size();
		if (len >= rhs.size())
		{
			erase(ministl::copy(rhs.start, rhs.finish, start), finish);
		}
		else
		{
			iterator mid = rhs.begin() + static_cast<difference_type>(len);
			ministl::copy(rhs.start, mid, finish);
			insert(start, mid, rhs.finish);
		}
	}
	return *this;
}
	deque& operator=(std::initializer_list<value_type> ilist)
	{
		deque tmp(ilist);
		swap(tmp);
		return *this;
	}
};

1.4.5 析构函数

template<class T,class Alloc = alloc, size_t BufSize = 0>
class deque {
	~deque()
	{
		if (map != nullptr)
		{
			clear();
			//先释放每个缓冲区 然后再释放map上的元素
			data_allocator::deallocate(*start.node, deque_buf_size(BufSize, sizeof(T));
			*start.node = nullptr;
			map_allocator::deallocate(map, map_size);
			map = nullptr;
		}
	}
};

1.5 deque容器的元素操作
1.5.1我们通过迭代器即可完成以下操作:

template<class T,class Alloc = alloc, size_t BufSize = 0>
class deque {
public:
	iterator begin() { return start; }
	iterator end() { return finish; }
	reference operator[](size_type n) {
		return start[difference_type(n)];
	}
	reference operator[](size_t n) {
		return start[difference_type(n)];
	}
	reference front() { return *start; }
	reference back() {
		iterator tmp = finish;
		--tmp;
		return *tmp;
	}
	size_type size() const { return finish - start; }
	bool empty() const { return finish == start; }
};

1.5.2 push_back()操作
在进行尾插的时候,我们需要判断最后缓冲区是否还有一个以上备用的空间,如果有的话就直接在备用空间上构造元素,然后调整最后缓冲区的使用状态:

template<class T,class Alloc = alloc, size_t BufSize = 0>
class deque {
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);
		}
	}
};

如果最后的空间不能够满足的话,我们就选择调用push_back_aux()函数来进行尾插法,push_back_aux()操作,我们在进行插入操作的时候,如果当前缓冲区已经不能够满足需求的话我们就调整map。

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中剩余的节点数目比较,如果添加量大于剩余量的话,就调整map
	*(finish.node + 1) = allocate_node();
	if (*(finish.node + 1) != nullptr) {
		//能开辟处节点在尾部用于存放元素的话,就更新最后一个节点的值然后跳到下一个缓冲区
		construct(finish.cur, t_copy);
		finish.set_node(finish.node + 1);
		finish.cur = finish.first;
	}
	else {
		//否则销毁当前节点
		deallocate_node(*(finish.node + 1));
	}
}

我们通过reserve_map_at_back()函数来进行map的调整:

template<class T, class Alloc, size_t BufSize>
void deque<T, Alloc, BufSize>::reserve_map_at_back(size_type nodes_to_add) {
	//判断你要添加的节点数目与map中剩余的节点数目比较,如果添加量大于剩余量的话,就调整map
	if (nodes_to_add + 1 > map_size - (finish.node - map))
		reallocate_map(nodes_to_add, false);
}

使用reallocate_map()来进行调整:

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数组的start指针,也就是开始的指针
	//如果满足条件还可以调整的话就继续调整,否则就重新开辟空间用来存放
	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)
			ministl::copy(start.node, finish.node + 1, new_nstart);
		else
			std::copy_backward(start.node, finish.node + 1, new_nstart + old_num_nodes);
	}
	else {
		size_type new_map_size = map_size + ministl::max(map_size, nodes_to_add) + 2;
		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);
		copy(start.node, finish.node + 1, new_nstart);
		map_allocator::deallocate(map, map_size);

		map = new_map;
		map_size = new_map_size;
	}
	//调整map数组中的开始和结束指针
	start.set_node(new_nstart);
	finish.set_node(new_nstart + old_num_ndoes - 1);
}

1.5.3 push_front()函数进行头插法,我们对于缓冲区进行判断,如果缓冲区还能插入元素的话就直接插入即可,否则就调用push_front_aux():

template<class T,class Alloc = alloc, size_t BufSize = 0>
class deque {
public:
	void push_front() {
		if (start.cur != start.first) {
			ministl::construct(start.cur - 1, t);
			--start.cur;
		}
		else {
			push_front_aux(t);
		}
	}
};

同样的当调用push_front_aux()时候,我们先调整好map,然后再进行插入操作:

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();
	if (*(start.node - 1) != nullptr) {
		start.set_node(start.node - 1);
		start.cur = start.last - 1;
		construct(start.cur, t_copy);
	}
	else {
		start.set_node(start.node - 1);
		start.cur = start.first;
		deallocate_node(*(start.node - 1));
	}
}

同样的reserve_map_at_front()也是进行调整,因为功能于上述类似因此就不多赘述:

template<class T, class Alloc, size_t BufSize>
void deque<T, Alloc, BufSize>::reserve_map_at_front(size_type nodes_to_add) {
	//判断尾端的节点是否是大于map中预留的节点个数,如果大于的话就重新开辟空间
	if (nodes_to_add + 1 > start.node - 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数组的start指针,也就是开始的指针
	//如果满足条件还可以调整的话就继续调整,否则就重新开辟空间用来存放
	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)
			ministl::copy(start.node, finish.node + 1, new_nstart);
		else
			std::copy_backward(start.node, finish.node + 1, new_nstart + old_num_nodes);
	}
	else {
		size_type new_map_size = map_size + ministl::max(map_size, nodes_to_add) + 2;
		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);
		copy(start.node, finish.node + 1, new_nstart);
		map_allocator::deallocate(map, map_size);

		map = new_map;
		map_size = new_map_size;
	}
	//调整map数组中的开始和结束指针
	start.set_node(new_nstart);
	finish.set_node(new_nstart + old_num_ndoes - 1);
}

1.5.4 pop_back()和pop_front()我们通过这两个函数来进行弹出操作,在判断弹出的时候,我们还是要判断是否还在当前缓冲区中,如果在当前缓冲区的话,那么就直接弹出即可,否则就调用pop_back_aux()函数或者是pop_front_aux()函数。

template<class T,class Alloc = alloc, size_t BufSize = 0>
class deque {
public:
	void pop_back() {
		if (finish.cur != finish.first) {
			--finish.cur;
			ministl::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();
		}
	}
}

我们调用的pop_back_aux()函数以及pop_front_aux()函数:

template<class T, class Alloc, size_t BufSize>
void deque<T, Alloc, BufSize>::pop_back_aux() {
	//销毁map指针所指向的最后一个节点
	deallocate_node(finish.first);
	finish.set_node(finish.node - 1);
	finish.cur = finish.last - 1;
	ministl::destroy(finish.cur);
}

template<class T, class Alloc, size_t BufSize>
void deque<T, Alloc, BufSize>::pop_front_aux() {
	//销毁map指针所指向的头一个节点
	ministl::destroy(start.cur);
	deallocate_node(start.first);
	start.set_node(start.node + 1);
	start.cur = start.first;
}

1.5.5 clear()操作
我们在对于deque进行clear操作的时候,将其用于清除整个deque,在deque的策略下,deque的最初状态应该是保存有一个缓冲区:

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

1.5.6 erase()操作的时候,有两种情况一种是我们将迭代器所指向的给析构掉,另外一种是将两个迭代器范围内的元素给全部都析构掉。

template<class T, class Alloc, size_t BufSize>
typename deque<T, Alloc, BufSize>::iterator deque<T, Alloc, BufSize>::erase(iterator pos) {
	iterator next = pos;
	++next;
	difference_type index = pos - start;
	//在进行map数组的调整的时候,判断其距离两头哪边更近一些,这样我们对近的那头进行插入拷贝等操作的话效率会高一些
	if (index < (size() >> 1)) {
		std::copy_backward(start, pos, next);
		pop_front();
	}
	else {
		copy(next, finish, pos);
		pop_back();
	}
	return start + index;
}

template<class T, class Alloc, size_t BufSize>
typename deque<T, Alloc, BufSize>::iterator 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) {//如果前方元素比较少
			std::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) {//以下将冗余的缓冲区释放
				date_allocator::deallocate(*cur, buffer_size());
			}
			start = new_start;
		}
		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;
	}
}

1.5.7 insert()操作,当我们进行插入操作的时候,当是在头部位置插入的时候,就调用头插法,如果在尾部插入的话就调用尾插法,否则就调用insert_aux()操作。

template<class T, class Alloc, size_t BufSize>
typename deque<T, Alloc, BufSize>::iterator deque<T, Alloc, BufSize>::insert(iterator position, const value_type& x) {
	if (position.cur == start.cur) {
		push_front(x);
		return start;
	}
	else if (position.cur == finish.cur) {
		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>::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;
		std::copy_backward(pos, back2, back1);//元素移动
	}
	*pos = x_copy;//在插入点上设定新值
	return pos;
}

2.stack容器

stack容器是一种先进后出的数据结构,deque是双向开口的数据结构,若以deque为底部结构并封闭其头端开口,便可以形成一个stack。

template <class T, class Container = ministl::deque<T>>
class stack {
	friend bool operator==(const stack& lhs, const stack& rhs);
	friend bool operator< (const stack& lhs, const stack& rhs);

public:
	typedef Container container_type;
	// 使用底层容器的型别
	typedef typename Container::value_type value_type;
	typedef typename Container::size_type size_type;
	typedef typename Container::reference reference;
	typedef typename Container::const_reference const_reference;

	//使用一个deque来完成其功能
private:
	container_type myctr;
public:
	//构造 复制 析构
	stack() = default;
	explicit stack(size_type n):myctr(n){
	}
	stack(size_type n, const value_type& value):myctr(n, value){
	}
	template <class IIter>
	stack(IIter first, IIter last)
		: c_(first, last)
	{
	}
	stack(std::initializer_list<T> llist):myctr(llist){
	}
	stack(const stack& s) :myctr(s.myctr) {
	}
	stack(const Container& c) :myctr(c) {
	}

	stack& operator=(const stack& s) {
		myctr = s.myctr;
		return *this;
	}

	stack& operator=(std::initializer_list<T> llist) {
		myctr = llist;
		return *this;
	}

	~stack() = default;

	reference top() {
		return myctr.back();
	}
	const_reference top() const {
		return myctr.back();
	}

	bool empty() const noexcept { 
		return myctr.empty(); 
	}
	size_type size() const noexcept { 
		return myctr.size(); 
	}
	void push(const value_type& value)
	{
		myctr.push_back(value);
	}
	void pop() {
		myctr.pop_back();
	}
	void clear()
	{
		while (!empty())
			pop();
	}
	void swap(stack& rhs) noexcept(noexcept(ministl::swap(myctr, rhs.myctr)))
	{
		ministl::swap(myctr, rhs.myctr);
	}
};

3.queue容器

queue是一种先进先出的数据结构,它有两个出口,同样的deque是双向开口的数据结构,若以deque为底部结构并封闭其低端的出口和前端嗯对入口即可。

class queue {
	friend bool operator==(const queue& lhs, const queue& rhs);
	friend bool operator< (const queue& lhs, const queue& rhs);

public:
	typedef Container container_type;
	typedef typename Container::value_type value_type;
	typedef typename Container::size_type size_type;
	typedef typename Container::reference reference;
	typedef typename Container::const_reference const_reference;

private:
	//使用一个deque来完成其功能
	container_type myctr;
public:
	queue() = default;
	explicit queue(size_type n):myctr(n){ 
	}
	queue(size_type n, const value_type& value) :myctr(n,value){
	}
	template <class Iterator>
	queue(Iterator first, Iterator last) : myctr(first, last) {
	}

	queue(std::initializer_list<T> llist) :myctr(llist.begin(), llist.end()) {
	}

	queue(const container_type& c) :myctr(c) {
	}
	queue(const queue& q) :myctr(q.myctr) {
	}

	queue& operator=(const queue& q) {
		myctr = q.myctr;
		return *this;
	}

	queue& operator=(std::initializer_list<T> llist) {
		myctr = llist;
		return *this;
	}

	~queue() = default;

	reference& front() {
		return myctr.front();
	}

	const_reference front() const {
		return myctr.front();
	}

	reference& back() {
		return myctr.back();
	}

	const_reference back() const {
		return myctr.back();
	}

	bool empty() const{
		return myctr.empty();
	}

	size_type size() const {
		return myctr.size();
	}

	void push(const value_type& value) {
		myctr.push_back(value);
	}

	void pop() {
		myctr.pop_front();
	}

	void clear()
	{
		while (!empty())
			pop();
	}

};

4.heap

heap并不属于STL容器组件,他扮演的是priority_queue的助手,priority_queue允许用户以任何次序将任何元素推入到容器中,但取出时一定是从优先权最高的元素开始取。binary max heap正是具有这样的特性,适合作为priority_queue的底层机制。
所谓binary heap就是一种完全二叉树,也就是说除了binary tree除了最底层的叶节点之外都是被填满的,而最底层的叶节点由左至右不得有空隙。
完全二叉树整棵树内没有任何漏洞,因此我们可以使用数组来存储所有节点。对于某一个节点来说,其如果位于数组的第i处,那么第2i处就是其左子节点,第2i+1处就是其右子节点,其父节点为于i/2处。
由此可得,我们仅仅需要一个数组和一组heap算法。

4.1 heap算法
为了满足完全二叉树的条件,新加入的元素一定要放在最下一层作为叶节点,并填补在由左至右的第一个空格,也就是将新元素插入在底层vector的end()处。
新元素是否适合于其现有位置我们通过一个上溯程序将新结点拿来将其父节点比较,如果其值比父节点大,就父子对换位置;如此一直上溯,直到不需对换或知道根节点为止。
4.1.1 push_heap算法

//push_heap算法
template<class RandomAccessIterator, class Distance, class T>
void _push_heap(RandomAccessIterator first, Distance holeIndex, Distance topIndex, T value) {
	Distance parent = (holeIndex - 1) / 2; //找出父节点
	while (holeIndex > topIndex && *(first + parent) < value) {
		//如果当前节点的序列号小于顶部序列号(顶部序列号一般为heap底层容器的最开始位置)
		//并且当前位置的值比父节点的值要大
		//那么就将父节点的值给子节点 
		*(first + holeIndex) = *(first + parent);
		//然后更新插入的节点的新位置 并更新其父节点
		holeIndex = parent;
		parent = (holeIndex - 1) / 2;
	}
	//在符合条件的位置上给当前节点赋值
	*(first + holeIndex) = value;
}

template<class RandomAccessIterator, class Distance, class T>
inline void push_heap_aux(RandomAccessIterator first, RandomAccessIterator last, Distance*, T*) {
	//新添加的值一定是为于容器最底端
	_push_heap(first, Distance((last - first) - 1), Distance(0), T(*(last - 1)));
}

//用两个迭代器来表示heap底部容器的头尾
template <class RandomAccessIterator>
inline void push_heap(RandomAccessIterator first, RandomAccessIterator last) {
	push_heap_aux(first, last, ministl::distance_type(first), ministl::value_type(first));
}

4.1.2 pop_heap()算法
pop操作取走根节点,为了满足完全二叉树的条件,必须将最下一层最右边的叶节点拿掉,我们执行了一个所谓的下溯程序:将根节点填入上述那个失去空间的叶节点值,再将它拿来和其两个子节点比较,并与较大子节点对调位置,如此一直下放。

//pop_heap算法
//这里的参数设定为:first设定为底部容器的最开始的位置 holeindex设定为要弹出的元素的位置
//len代表的是调整后的底部容器的长度,最后的value代表的是原来底部容器中最尾端的位置
template<class RandomAccessIterator, class Distance, class T>
void _adjust_heap(RandomAccessIterator first, Distance holeIndex, Distance len, T value) {
	Distance topIndex = holeIndex;
	//调整的节点的右节点
	Distance secondChild = 2 * holeIndex + 2;
	while (secondChild < len) {
		//比较左右两个节点的值,然后以secondChile表示较大的那一个
		if (*(first + secondChild) < *(first + (secondChild - 1)))
			--secondChild;
		*(first + holeIndex) = *(first + secondChild);
		holeIndex = secondChild;
		secondChild = 2 * (secondChild + 1);
	}
	if (secondChild == len) {
		*(first + holeIndex) = *(first + (secondChild - 1));
		holeIndex = secondChild - 1;
		holeIndex = secondChild - 1;
	}
	_push_heap(first, holeIndex, topIndex, value);
}

template <class RandomAccessIterator, class T, class Distance>
inline void _pop_heap(RandomAccessIterator first, RandomAccessIterator last,
	RandomAccessIterator result, T value, Distance*) {
	*result = *first;
	_adjust_heap(first, Distance(0), Distance(last - first), value);
}

//pop操作的结果应为底部容器的第一个元素,因此,首先设定欲调整值为尾值,然后将首值调至
//尾结点(所以将迭代器result设为last-1),然后调整【first,last-1】,使之重新成一个合格的heap
template<class RandomAccessIterator, class T>
inline void _pop_heap_aux(RandomAccessIterator first, RandomAccessIterator last, T*) {
	_pop_heap(first, last - 1, last - 1, T(*(last - 1)), ministl::distance_type(first));
}

template<class RandomAccessIterator>
inline void pop_heap(RandomAccessIterator first, RandomAccessIterator last) {
	_pop_heap_aux(first, last, ministl::value_type(first));
}

4.1.3 sort_heap()算法
因为每次pop_heap()可获得heap中最大值,如果持续做pop_heap()操作,每次将操作范围从后向前缩减一个元素(因为pop_heap会把值最大的元素放在底部容器的最尾端),当pop完毕后就可以获得一个递增序列。

//sort_heap算法
template<class RandomAccessIterator>
void sort_heap(RandomAccessIterator first, RandomAccessIterator last) {
	while (last - first > 1)
		pop_heap(first, last--);
}

4.1.4 make_heap()算法
以此来讲一个现有的数据化为一个heap

//make_heap算法
template<class RandomAccessIterator, class T, class Distance>
void _make_heap(RandomAccessIterator first, RandomAccessIterator last, T*, Distance*) {
	if (last - first < 2)
		return;
	Distance len = last - first;
	//找出第一个需要重拍的子树头部,以parent标识出
	Distance parent = (len - 2) / 2;
	while (true) {
		//重新以parent为首的子树,len是为了用于判断操作范围
		_adjust_heap(first, parent, len, T(*(first + parent)));
		if (parent == 0) return;
		parent--;
	}
}

template<class RandomAccessIterator>
inline void make_heap(RandomAccessIterator first, RandomAccessIterator last) {
	_make_heap(first, last, ministl::value_type(first), ministl::distance_type(first));
}

5.priority_queue容器(大顶堆)

priority_queue是一个有权值观念的queue,他允许加入新元素,移除旧元素,审视元素等功能,并且由于其带有权值,因此内部会依据权值来对元素排列,权值最高的排在最前面。
同样的其底部是通过一个vector以及heap算法来实现的,因此由于上述已经实现了heap算法,以下直接展示代码:

template <class T, class Container = ministl::vector<T>,
	class Compare = ministl::less<T>>
class priority_queue {
	friend bool operator==(const priority_queue& lhs, const priority_queue& rhs);
	friend bool operator!=(const priority_queue& lhs, const priority_queue& rhs);

public:
	typedef Container container_type;
	typedef Compare compare_type;

	typedef T value_type;
	typedef size_t size_type;
	typedef T& reference;
	typedef const reference const_reference;

private:
	container_type myctr;
	compare_type mycmp;

public:
	// 构造、复制、移动函数
	priority_queue() = default;
	priority_queue(const Compare& c):myctr(),mycmp(c) {
	}
	explicit priority_queue(size_type n):myctr(n) {
		ministl::make_heap(myctr.begin(), myctr.end(), mycmp);
	}
	priority_queue(size_type n, const value_type& value):c_(n, value)
	{
		ministl::make_heap(myctr.begin(), myctr.end(), mycmp);
	}
	template <class Iterator>
	priority_queue(Iterator first, Iterator last): myctr(first, last)
	{
		ministl::make_heap(myctr.begin(), myctr.end(), mycmp);
	}
	priority_queue(std::initializer_list<T> llist):myctr(ilist)
	{
		ministl::make_heap(myctr.begin(), myctr.end(), mycmp);
	}
	priority_queue(const Container& s)
		:myctr(s)
	{
		ministl::make_heap(myctr.begin(), myctr.end(), mycmp);
	}
	priority_queue(const priority_queue& rhs)
		:myctr(rhs.myctr), mycmp(rhs.mycmp)
	{
		ministl::make_heap(myctr.begin(), myctr.end(), mycmp);
	}
	priority_queue& operator=(const priority_queue& rhs)
	{
		myctr = rhs.myctr;
		mycmp = rhs.mycmp;
		ministl::make_heap(myctr.begin(), myctr.end(), mycmp);
		return *this;
	}
	priority_queue& operator=(std::initializer_list<T> llist)
	{
		myctr = llist;
		mycmp = compare_type();
		ministl::make_heap(myctr.begin(), myctr.end(), mycmp);
		return *this;
	}
	~priority_queue() = default;
public:
	const_reference top() const { return myctr.front(); }
	bool empty() const { return myctr.empty(); }
	size_type size() const { return myctr.size(); }
	void push(const value_type& value)
	{
		c_.push_back(value);
		ministl::push_heap(myctr.begin(), myctr.end(), mycmp);
	}
	void pop()
	{
		ministl::pop_heap(myctr.begin(), myctr.end(), mycmp);
		myctr.pop_back();
	}
	void clear()
	{
		while (!empty())
			pop();
	}
	void swap(priority_queue& rhs) noexcept
	{
		ministl::swap(myctr, rhs.myctr);
		ministl::swap(mycmp, rhs.mycmp);
	}
	
};

参考:侯捷老师《STL源码剖析》 TinySTL

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值