STL — vector源代码剖析

栏目: 编程工具 · 发布时间: 6年前

vector源代码剖析

vector的数据安排以及操作方式,与array非常相似. 两者的唯一差别在于空间的运用的灵活性. array是静态空间,一旦配置了就不能够再改变. 要换

大一点的房子,可以,一切琐细得由客户端自己来: 首先配置一块新空间,然后将元素从旧址一一搬往新址,再把原来的空间释放还给系统. vector

是动 态空间,随着元素的加入,它的内部机制会自行扩充空间以容纳新元素. 因此,vector的运用该对于内存的合理运用与运用的灵活性有很大的帮

助,我们 再也不必害怕空间不足而一开始就要求一个大块头的array了,我们可以安心地使用vector,吃多少用多少.

vector的实现技术,关键在于其对大小的控制以及重新配置时的数据移动效率。 一旦vector旧有空间满载,如果客户端每增加一个元素,vector内部

是扩充了一个元素的空间,实为不智,因为所谓扩充空间,一如稍早所说,是""配置新空间/数据移动/释放旧空间"的大工程,时间成本很高,应该加

某种未雨绸缪的考虑,稍后我们便可以看到SGI vector的空间配置策略. 首先看看vector的源代码摘要:

template <class T, class Alloc = alloc>
class vector {
public:

	//迭代器的五种类型
	typedef T value_type;
	typedef value_type* pointer;
	typedef const value_type* const_pointer;

	typedef value_type* iterator;
	typedef const value_type* const_iterator;

	typedef value_type& reference;
	typedef const value_type& const_reference;

	typedef size_t size_type;

	typedef ptrdiff_t difference_type;

#ifdef __STL_CLASS_PARTIAL_SPECIALIZATION
	typedef reverse_iterator<const_iterator> const_reverse_iterator;
	typedef reverse_iterator<iterator> reverse_iterator;
#else /* __STL_CLASS_PARTIAL_SPECIALIZATION */
	typedef reverse_iterator<const_iterator, value_type, const_reference,
		difference_type>  const_reverse_iterator;
	typedef reverse_iterator<iterator, value_type, reference, difference_type>
		reverse_iterator;
#endif /* __STL_CLASS_PARTIAL_SPECIALIZATION */
protected:

	//在这里就用到了我们的空间配置器.
	typedef simple_alloc<value_type, Alloc> data_allocator; //前面是有博客的

	//这里vector维护3个迭代器. 分别指向申请到内存的头部,vector利用到的地方 和 vector申请到内存结束的地方.
	iterator start; //头.
	iterator finish;//使用位置.
	iterator end_of_storage;//申请到内存结束的地方.


	void insert_aux(iterator position, const T& x);
	void deallocate() {
		if (start) data_allocator::deallocate(start, end_of_storage - start);
	}

	void fill_initialize(size_type n, const T& value) {
		start = allocate_and_fill(n, value);
		finish = start + n;
		end_of_storage = finish;
	}
public:
	//这些begin end rend rbegin 不解释!
	iterator begin() { return start; }
	const_iterator begin() const { return start; }
	iterator end() { return finish; }
	const_iterator end() const { return finish; }
	reverse_iterator rbegin() { return reverse_iterator(end()); }
	const_reverse_iterator rbegin() const {
		return const_reverse_iterator(end());
	}
	reverse_iterator rend() { return reverse_iterator(begin()); }
	const_reverse_iterator rend() const {
		return const_reverse_iterator(begin());
	}

	size_type size() const { return size_type(end() - begin()); } //元素个数
	size_type max_size() const { return size_type(-1) / sizeof(T); } //最大元素个数
	size_type capacity() const { return size_type(end_of_storage - begin()); } //容量
	bool empty() const { return begin() == end(); } //是否为空
	reference operator
		[](size_type n) {
		return *(begin() + n);
	}
	const_reference operator[](size_type n) const { return *(begin() + n); } //支持随机访问.

	vector() : start(0), finish(0), end_of_storage(0) {}
	vector(size_type n, const T& value) { fill_initialize(n, value); }
	vector(int n, const T& value) { fill_initialize(n, value); }
	vector(long n, const T& value) { fill_initialize(n, value); }
	explicit vector(size_type n) { fill_initialize(n, T()); }

	vector(const vector<T, Alloc>& x) {
		start = allocate_and_copy(x.end() - x.begin(), x.begin(), x.end());
		finish = start + (x.end() - x.begin());
		end_of_storage = finish;
	}

#ifdef __STL_MEMBER_TEMPLATES
	template <class InputIterator>
	vector(InputIterator first, InputIterator last) :
		start(0), finish(0), end_of_storage(0)
	{
		range_initialize(first, last, iterator_category(first));
	}
#else /* __STL_MEMBER_TEMPLATES */
	vector(const_iterator first, const_iterator last) {
		size_type n = 0;
		distance(first, last, n);
		start = allocate_and_copy(n, first, last);
		finish = start + n;
		end_of_storage = finish;
	}
#endif /* __STL_MEMBER_TEMPLATES */
	~vector() { //这是vector的一个member function
		destroy(start, finish);
		deallocate();
	}

	reference front() { return *begin(); } //第一个元素的引用.
	const_reference front() const { return *begin(); } 
	reference back() { return *(end() - 1); } //最后一个元素的引用.
	const_reference back() const { return *(end() - 1); }
	void push_back(const T& x) {
		if (finish != end_of_storage) {
			construct(finish, x);
			++finish;
		}
		else
			insert_aux(end(), x);
	}
	void insert(iterator pos, size_type n, const T& x);
	void pop_back() {
		--finish;
		destroy(finish);
	}
	iterator erase(iterator position) {
		if (position + 1 != end())
			copy(position + 1, finish, position);
		--finish;
		destroy(finish);
		return position;
	}
	void resize(size_type new_size) { resize(new_size, T()); }
	void clear() { erase(begin(), end()); }

protected:
	//配置空间并且填满内容.
	iterator allocate_and_fill(size_type n, const T& x) {
		iterator result = data_allocator::allocate(n);
		__STL_TRY{
			uninitialized_fill_n(result, n, x);
			return result;
		}
		__STL_UNWIND(data_allocator::deallocate(result, n));
	}
vector维护的是一块连续的线性空间,所以无论其元素型别为何,普通指针都可以作为vector的迭代器而满足所有必要条件,因为vector迭代器所需要

的操作行为,如operator*,operator->,operator++,operator--,operator+,operator+=,operator-=,普通指针天数就具备. vector支持随机存取,

而普通指针正有这种能力. 所以,vector提供的是Random Access Iterators(如果不知道这个类型,可以看这个:

我们在上面可以看到: typedef value_type* iterator; //vector的迭代器是普通指针.

vector所采用的数据结构都非常的简单:线性连续空间.它以两个迭代器strat和finish分别指向配置得来的连续空间中目前已被使用的范围,并以迭代

器end_of_storage.指向整块连续空间的尾端.

为了降低空间配置时的速度成本,vector实际配置的大小可能比客户端需求量更大一些,以备将来可能的扩充,这便是容量的观念. 一个vector的容量

永远大于或等于其大小,一旦容量等于大小,便是满载,下次再新增元素,整个vector就得另寻居所了.

运用start,finish,end_of_storage三个迭代器,便可轻易地提供首位标示,大小,容量,空容器判断,标注运算子,最前端元素值,最后的元素值等

STL — vector源代码剖析

好的我们继续往下走,vector的构造和内存管理. 其实都可以在刚刚的源代码摘要中找到核心内容. 我们发现vector缺省使用alloc作为空间配置器,

并据此另外定义一个data_allocator,为的是更方便以元素大小为配置单位:(如果对空间配置器不够了解可以看这篇博客: 源码剖析空间配置器)

typedef simple_alloc<value_type, Alloc> data_allocator;

所以呢,data_allocator::allocate(n) 表示配置n个元素空间.

vector提供许多constructors,其中一个允许我们指定空间大小及初值.

vector(size_type n, const T& value) { fill_initialize(n, value); }

void fill_initialize(size_type n, const T& value) {//填充并初始化
	start = allocate_and_fill(n, value);
	finish = start + n;
	end_of_storage = finish;
}

iterator allocate_and_fill(size_type n, const T& x) {//配置而后填充
	iterator result = data_allocator::allocate(n);
	__STL_TRY{
		uninitialized_fill_n(result, n, x);
		return result;
	}
	__STL_UNWIND(data_allocator::deallocate(result, n));
}
uninitialized_fill_n()会根据第一参数的型别特征决定使用算法fill_n()或反复调用construct()来完成任务.(这个函数也非常重要,如果有不明白

的人类! 可以看这个博客:  内存基本处理 工具 函数源码剖析 ) 当我们的push_back()将新元素插入于vector尾端时,该函数首先检查是否还有备用空

间,如果有就直接在备用空间上构造元素,并调整迭代器finish 使vector变大. 如果没有备用空间了,就扩充空间(重新配置,移动数据,释放原空

间).

void push_back(const T& x) {
	if (finish != end_of_storage) {
		construct(finish, x);
		++finish;
	}
	else
		insert_aux(end(), x);
}

template <class T, class Alloc>
void vector<T, Alloc>::insert_aux(iterator position, const T& x) {
	if (finish != end_of_storage) { //还有备用空间
		//在备用空间起始处构造一个函数,并以vector最后一个元素值为其初值.
		construct(finish, *(finish - 1));
		++finish;
		T x_copy = x;
		copy_backward(position, finish - 2, finish - 1);
		*position = x_copy;
	}
	else { //已无备用空间

		const size_type old_ size = size();
		const size_type len = old_size != 0 ? 2 * old_size : 1; //这句仔细看!!!!
		iterator new_start = data_allocator::allocate(len); //实际配置的空间.
		iterator new_finish = new_start;
		__STL_TRY{
			//将原vector的内容拷贝至新的vector.
			new_finish = uninitialized_copy(start, position, new_start);
			//为新元素设定初值x.
			construct(new_finish, x);
			++new_finish;

			//将安插点的原内容也拷贝过来.
			new_finish = uninitialized_copy(position, finish, new_finish);
		}

#       ifdef  __STL_USE_EXCEPTIONS 
		catch (...) {
			//析构并释放原vector
			destroy(new_start, new_finish);
			data_allocator::deallocate(new_start, len);
			throw;
		}
#       endif /* __STL_USE_EXCEPTIONS */

		//调整迭代器,指向新的vector.
		destroy(begin(), end());
		deallocate();
		start = new_start;
		finish = new_finish;
		end_of_storage = new_start + len;
	}
}
注意,所谓动态增加大小,并不是在原空间之后接连续新空间,而是以原大小的两倍另外配置一块较大空间,然后将原内容拷贝过来,然后才开始在原

内容之后构造新元素,并释放原空间,因此,对任何vector的任何操作,一旦引起空间重新配置,指向原vector的所有迭代器就都失效了. 这是程序

员容易犯的错误,务必小心. 下面是插入的过程: (注意看一共分几种情况,然后各种情况下都是如何解决的)!

template <class T, class Alloc>
//从position开始,插入n个元素,元素初值为x。
void vector<T, Alloc>::insert(iterator position, size_type n, const T& x) {
	if (n != 0) { //当n != 0的时候 才进行以下所有操作
		if (size_type(end_of_storage - finish) >= n) {
			//备用空间大于 等于 "新增元素个数"
			T x_copy = x;
			//以下计算插入点之后的现有元素个数
			const size_type elems_after = finish - position;
			iterator old_finish = finish;
			if (elems_after > n) {
				// 插入点之后的现有元素个数 大于 新增元素个数
				uninitialized_copy(finish - n, finish, finish);
				finish += n;
				copy_backward(position, old_finish - n, old_finish);
				fill(position, position + n, x_copy);
			}
			else {
				// 插入点之后的现有元素个数 小于等于 新增元素个数
				uninitialized_fill_n(finish, n - elems_after, x_copy);
				finish += n - elems_after;
				uninitialized_copy(position, old_finish, finish);
				finish += elems_after;
				fill(position, old_finish, x_copy);
			}
		}
		else {
			//备用空间小于 "新增元素个数"
			//首先决定新长度: 旧长度的两倍,或旧长度+新增元素个数.
			const size_type old_size = size();
			const size_type len = old_size + max(old_size, n);

			//以下配置新的vector空间.
			iterator new_start = data_allocator::allocate(len);
			iterator new_finish = new_start;
			__STL_TRY{
				//以下首先将旧vector的插入点之前的元素复制到新空间
				new_finish = uninitialized_copy(start, position, new_start);
				//以下再将新增元素填入新空间.
				new_finish = uninitialized_fill_n(new_finish, n, x);
				//以下再将就vector的插入点之后的元素复制到新空间.
				new_finish = uninitialized_copy(position, finish, new_finish);
			}
#         ifdef  __STL_USE_EXCEPTIONS 
			catch (...) {
				//以下清楚并释放旧的vector
				destroy(new_start, new_finish);
				data_allocator::deallocate(new_start, len);
				throw;
			}
#         endif /* __STL_USE_EXCEPTIONS */
			destroy(start, finish);
			deallocate();
			start = new_start;
			finish = new_finish;
			end_of_storage = new_start + len;
		}
	}
}
更多全面的stl_vector源码->我的github: stl_vector源代码  大家尽情的阅读吧.

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

网站运维技术与实践

网站运维技术与实践

饶琛琳 / 电子工业出版社 / 2014-3 / 69.00元

网站运维工作,一向以内容繁杂、覆盖面广著称。《网站运维技术与实践》选取日常工作涉及的监测调优、日志分析、集群规划、自动化部署、存储和数据库等方面,力图深入阐述各项工作的技术要点及协议原理,并介绍相关开源产品的实践经验。在技术之外,作者也分享了一些关于高效工作及个人成长方面的心得。 《网站运维技术与实践》适合Linux 系统管理员、中大型网站运维工程师及技术负责人、DevOps 爱好者阅读。同......一起来看看 《网站运维技术与实践》 这本书的介绍吧!

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器