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源代码 大家尽情的阅读吧.
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Coding the Matrix
Philip N. Klein / Newtonian Press / 2013-7-26 / $35.00
An engaging introduction to vectors and matrices and the algorithms that operate on them, intended for the student who knows how to program. Mathematical concepts and computational problems are motiva......一起来看看 《Coding the Matrix》 这本书的介绍吧!