vector概述
vector的数据安排以及操作方式与array非常相似,两者的唯一差别在于空间的运用的灵活性,array是静态空间,一旦配置了就不能改变,vector是动态空间,随着元素的加入,它的内部机制会自行扩充空间以容纳新元素
vector的迭代器
vector维护的是一个连续线性空间,所以不论其元素型别为何,普通指针都可以作为vector的迭代器而满足所有必要条件,所以vector提供的是Random Access Iterators
template <class T,class Alloc=alloc>
class vector
{
typedef T value_type;
typedef value_type* iterator;
};
vector的数据结构
vector所采用的数据结构非常简单:线性连续空间。它以两个迭代器start和finish分别指向配置得来的连续空间中目前已被使用的范围,并以迭代器end_of_storage指向整块连续空间的尾端:
template <class T,class Alloc =alloc>
class vector
{
protected:
iterator start;
iterator finish;
iterator end_of_storage;
};
运用start,finish,end_of_storage三个迭代器,便可轻易地提供首尾标示,大小,容量,空容器判断,注标([])运算子等功能
template <class T,class Alloc=alloc>
class vector
{
public:
iterator begin() { return start;}
iterator end() { return finish;}
size_type size() const { return size_type(end()-begin()); }
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);
}
reference front()
{
return *begin();
}
reference back()
{
return *(end()-1);
}
};
vector的构造与内存管理
vector提供许多constructs,其中一个允许我们指定空间大小及初值:
//构造函数,允许指定vector大小n和初值value
vector(size_type n,const T& value)
{
fill_initialize(n,value);
}
//填充并予以初始化
void fill_initialize(size_type n,const T& value)
{
start =allocte_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);
unititalized_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(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;
//以上配置原则:如果原大小为0,则配置1个元素大小
//如果原大小不为0,则配置原大小的两倍
//前半段用来放置原数据,后半段准备用来放置新数据
iterator new_start=data_allocator::allocate(len);
iterator new_finish=new_start;
try
{
new_finish=uninitialized_copy(start,position,new_start);
construct(new_finish,x);
++new_finish;
//将原vector的备用空间中的内容也忠实拷贝过来
new_finish=uninitialized_copy(position,finish,new_finish);
}
catch(...)
{
destroy(new_start,new_finish);
data_allocator::deallocate(new_start,len);
throw;
}
//析构并释放原vector
destroy(begin(),end());
deallocate();
//调整迭代器,指向新vector
start=new_start;
finish=new_finish;
end_of_storage=new_start+len;
}
}
所谓的动态增加大小,并不是在原空间之后接续新空间(因为无法保证原空间之后尚有可供配置的空间),而是以原大小的两倍另外配置一块较大空间,然后将原内容拷贝过来,然后才开始在原内容之后构造新元素,并释放原空间,因此,对vector的任何操作,一旦引起空间重新配置,指向原vector的所有迭代器都失效了
vector的元素操作
//将尾端元素拿掉,并调整大小
void pop_back()
{
--finish;//将尾端标记前移一格,表示将放弃尾端元素
destroy(finish);
}
//清除[first,last)中的所有元素
iterator erase(iterator first,iterator last)
{
iterator i=copy(last,finish,first);
destroy(i,finish);
finish=finish-(last-first);
return first;
}
下面是vector::insert()实现内容:
//从position开始,插入n个元素,元素初值为x
template <class T,class Alloc>
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);
finsih+=elems_after;
fill(position,old_finish,x_copy); }
}
}
else
{
//备用空间小于新增元素个数(那就必须配置额外的内存)
//首先决定新长度:旧长度的两倍,或旧长度+新增元素个数
const size_type old_size=size();
const size_ype len=old_size+max(old_size,n);
//以下配置新的vector空间
iterartor 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_copy_n(new_finish,n,x);
new_finish=uninitialized_copy(psition,finish,new_finish);
}
}
#ifdef _STL_USE_EXCEPTIONS
catch(...)
{
destroy(new_start,new_finish);
data_allocator::deallocate(new_start,len);
throw;
}
#endif
destroy(start,finish);
deallocate();
deallocate();
start=new_start;
finsih=new_finish;
end_of_storage=new_start+len;
}
}
}