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三个迭代器,便可轻易地提供首位标示,大小,容量,空容器判断,标注运算子,最前端元素值,最后的元素值等
好的我们继续往下走,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源代码 大家尽情的阅读吧.
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
A Common-Sense Guide to Data Structures and Algorithms
Jay Wengrow / Pragmatic Bookshelf / 2017-8-13 / USD 45.95
If you last saw algorithms in a university course or at a job interview, you’re missing out on what they can do for your code. Learn different sorting and searching techniques, and when to use each. F......一起来看看 《A Common-Sense Guide to Data Structures and Algorithms》 这本书的介绍吧!