STL之Vector源码剖析
vevtor与array非常相似,两者唯一差别在于空间运用的灵活性。array是静态空间,一旦配置了就不能改变;vector是动态空间,随着元素的加入,它的内部机制会自行扩充空间以容纳新元素。
vector的实现技术关键在于其对大小的控制以及重新配置时数据移动效率。一旦vector旧有空间满载,如果客户端每新增一个元素,vector内部知识扩充一个元素的空间,实为不智,因为所谓扩充空间,是”配置新空间/移动数据/释放旧空间”的大工程,时间成本很高,故应该加入某种未雨绸缪的考虑。
vector迭代器
vector维护的是一个连续线性空间。它提供的是Random Access Iterators,普通指针都可作为vector的迭代器。
template<class T, class Alloc= alloc>
classvector{
public:
typedef T value_type;
typedef value_type* iterator; //vector的迭代器时普通指针
...
};
根据上述定义,如果客户端写出这样的代码:
vector<int>::iterator ivite;
vector<Shape>::iterator svite;
ivite的型别其实就是int*,svite的型别其实就是Shape*。
vector的数据结构
vector所采用的数据结构非常简单:线性连续空间。它以两个迭代器start和finish分别指向配置得来的连续空间中目前已被使用的范围,并以迭代器end_of_storage指向整块连续空间的尾端。
template<class T, class Alloc= alloc>
classvector{
...
protected:
iteratorstart; //表示目前使用空间的头
iteratorfinish; //表示目前使用空间的尾
iteratorend_of_storage;//表示目前可使用空间的尾
...
};
可运用上面三个迭代器轻松实现以下vector的成员函数:
template<class T, class Alloc= alloc>
classvector{
...
public:
iteratorbegin(){ return start;}
iteratorend(){ return finish;}
size_typesize() const{ returnsize_type(end() - begin());}
size_typecapacity() const{
returnsize_type(end_of_storage - begin());}
bool empty(){ returnbegin() == end();}
referenceoperator[](size_type n){ return *(begin() + n);}
referencefront(){ return *begin();}
referenceback(){ return *end();}
...
};
vector的构造与内存管理:constructor,push_back
vector缺省使用alloc作为空间配置器,并据此另外定义了一个data_allocator,为的是方便元素大小为配置单位:
template<class T, class Alloc= alloc>
classvector{
protected:
typedef simple_alloc<value_type, Alloc>data_allocator;
...
};
vector提供了许多constructors,其中一个允许我们制定空间大小及初值:
//构造函数,允许指定vector大小n和处置value
vector<size_type n,const T& value>{ fill_initialize(n,value);}
//填充并予以初始化
voidfill_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)
{
iteratorresult = data_allocator::allocate(n);
uninitialize_fill_n(result,n, x);
return result;
}
当我们以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(iteratorposition, const T& x) {
if (finish !=end_of_storage) { //还有备用空间
//在备用空间起始处构造一个元素,并以vectror最后一个元素值为其初值
construct(finish,*(finish - 1));
// 调整水位
++finish;
//在position后面元素向后移动,并在position插入元素
T x_copy = x;
copy_backward(position,finish - 2, finish - 1);
*position = x_copy;
}
else { //已无备用空间
const size_typeold_size = size();
const size_type len =old_size != 0 ? 2 * old_size : 1;
//以上配置原则:如果原大小为0,则配置1(个元素大小)
// 如果原大小不为0,则配置原大小的两倍
// 前半段用来放置原数据,后半段准备用来放置新数据
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(...) {
// "commitor rollback" 语意:若非全部成功,则一个不留
destroy(new_start,new_finish);
data_allocator::deallocate(new_start,len);
throw;
}
# endif /*__STL_USE_EXCEPTIONS */
// 析构并释放vector
destroy(begin(),end());
deallocate();
// 调整迭代器,指向vector
start = new_start;
finish = new_finish;
end_of_storage =new_start + len;
}
}
注意,所谓动态增加大小,并不是在原空间之后持续新空间,而是以原大小的两倍另外配置一块较大空间,然后将原内容拷贝过来,然后才开始在原内容之后构造新元素,并释放原空间。因此,对vector的任何操作,一旦引起空间重新配置,指向原vector的所有迭代器就都失效了。
vector的元素操作:pop_back,erase,clear,insert
//将尾端元素拿掉,并调整大小
void pop_back() {
--finish;
destroy(finish);
}
//清除[first,last]中的所有元素
iterator erase(iteratorfirst, iterator last) {
iterator i =copy(last, finish, first); //copy是全局函数
destroy(i,finish); //全局函数
finish = finish - (last - first);
return first;
}
//清除某个位置上的元素
iterator erase(iterator position) {
if (position + 1 !=end()) //如果p不是指向最后一个元素
//将p之后的元素一一向前移动
copy(position + 1, finish, position);
--finish; // 调整水位
destroy(finish); // 全局函数
return position;
}
//清除全部元素。注意并未释放空间,以备未来可能还有加入新元素
void clear() { erase(begin(),end()); }
下图展示了erase[first,last]的操作
下面是vector::insert的实现内容:
//从position开始,插入n个元素,元素初值为x
template <class T, class Alloc>
void vector<T, Alloc>::insert(iteratorposition, 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_typeelems_after = finish - position;
iterator old_finish= finish;
if (elems_after > n) {
// “插入点之后的现有元素个数”大于新增元素个数”
uninitialized_copy(finish- n, finish, finish);
finish += n; // 将vector尾端标记后移
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_typeold_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(...) {
//如果异常发生,实现 "commitor rollback" semantics.
destroy(new_start,new_finish);
data_allocator::deallocate(new_start,len);
throw;
}
# endif /*__STL_USE_EXCEPTIONS */
// 以下清除并释放旧的vector
destroy(start,finish);
deallocate();
// 以下调整水位
start = new_start;
finish = new_finish;
end_of_storage =new_start + len;
}
}
}
下面几个图展示了insert[position,n,x]的操作