vector的使用就像数组,但是vector的空间是可变的。vector是动态空间,随着元素的加入,它的内部机制会自行扩充空间以容纳新元素。vector的实现技术,关键就在于对大小的控制以及重新配置时的数据移动效率。需要注意的是,对于空间的扩充,是(配置新空间->数据移动->释放旧空间)的大工程,时间成本是较高的,因此如果每次需要扩充仅仅只扩充所需的,则会导致频繁的扩充,因此vector采用了未雨绸缪的考虑。
学习vector的核心在于理解 finish和end_of_storage的区别, 即size和capacity的区别,还有如何进行的空间扩充机制,同时,insert函数也处处都是细节,非常值得学习
vector的class定义: (重要和复杂的函数之后详细展开) (诸多思路都在注释中)
template<class T,class Alloc=alloc>
class vector{
public:
//用typedef定义一些变量
typedef T value_type;
typedef value_type* pointer; //指针
typedef value_type* iterator; //迭代器
typedef value_type& reference;
typedef size_t size_type;
typedef ptrdiff_t difference_type;
protected:
typedef simple_alloc<value_type,Alloc> data_allocator;//空间配置器
/*----------vector的三个核心变量------------------------------*/
iterator start; //目前使用容器的头
iterator finish; //目前使用空间的尾
iterator end_of_storage; //目前可用空间的尾
/*-------------注意区分finish和end_of_storage-----------------*/
void insert_aux(iterator position,const T&x);
void deallocate();
void fill_initialize(size_type n,const T&value);
iterator allocate_and_fill(size_type n,const T&x){
iterator result=data_allocator::allocate(n);
uninitialized_fill_n(result,n,x);
return result;
}
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():start(0),finish(0),end_of_storage(0){}
vector(size_type t,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(){
destroy(start,finish); //先析构
deallocate(); //再释放空间
}
//push pop //插入和释放元素
void push_back(const T& x);
void pop_back();
iterator erase(iterator position); //消除某个位置上的元素
//重新规划大小
void resize(size_type new_size,const T& x); //非常重要
void resize(size_type new_size){resize(new_size,T());}
void clear(){erase(begin(),end());} //消除空间
}
vector维护的是一个连续线性空间,所有不论其元素类型为何,普通指针都可以作为vector的迭代器而满足所有必要条件。vector支持随机存储,因此vector提供的是 Random Access Iterators
vector为了维护连续的线性空间,以两个迭代器start何finish分别指向配置得来的连续空间目前已经被使用的范围,并以迭代器end_of_storage指向整个连续空间(含备用空间)的尾端。为了避免频繁的空间扩充导致性能下降,vector实际的配置大小可能比客户端需求量要更大一些,以备将来的扩充,也就是 capacity>=size;一个vector的容量永远大于或等于其大小,一旦容量等于大小,便满载了,之后有新增元素就必须重新resize。
通过begin,finish,end_of_storage三个变量,便可以达到提供首位的标识、vector的大小、容量、空容器判断等诸多机制。
下面这段很简单代码说明vector的size和capacity的不同: (g++编译器)
vector<int> v;
cout << "size:" << v.size() << ",capacity:" << v.capacity() << endl;
for (int i = 0; i < 10;i++)
{
v.push_back(i);
cout << "size:" << v.size() << ",capacity:" << v.capacity() << endl;
}
/*------------------------
size:0,capacity:0
size:1,capacity:1
size:2,capacity:2
size:3,capacity:4
size:4,capacity:4
size:5,capacity:8
size:6,capacity:8
size:7,capacity:8
size:8,capacity:8
size:9,capacity:16
size:10,capacity:16
----------------------*/
tips:为了简洁在下面代码中都没有加类限定符
void resize(size_type new_size,const T& x)
{
if(new_size<size()) //要变的尺寸更小,则把之后多余的消去
erase(begin()+new_size,end());
else
insert(end(),new_size-size(),x); //用x填充之后增加的数据
}
当向vector尾部插入元素时,首先检查是否还有备用空间,如果有备用空间则直接在尾部构造元素即可,并调整迭代器finish位置;如果没有备用空间,则需要扩充空间(重新配置、移动数据、释放原空间)
void push_back(const T& x)
{
if(finish!=end_of_storage){
construct(finish,x); //在finish处赋值为x
finish++; //调整finish位置
}
else
{
insert_aux(end(),x); //扩充空间并插入元素
}
}
insert_aux函数是protected类型成员,是vector的push_back实现的基础,即动态扩容的基础。
动态增加大小,并不是在原空间之后接续新的空间,因为无法保证原空间之后尚有可配置的空间,而是以原大小的两倍另外配置一块较大的空间,然后将内容拷贝过来,在新空间上原内容之后构造新元素,释放原空间。
因此,注意:对vector的任何操作,一旦引起空间重新配置,则指向原vector的所有指针迭代器都失效。
void insert_aux(iterator position,const T&x)
{
if(finish!=end_of_storage) //后面还有备用空间
{
//则在备用空间出构造一个元素,以vector的最后一个元素作为初值初始化该新位置的元素值
construct(finish,*(finish-1));
++finish; //调整finish迭代器
//将position到最后的元素向后移动,为position处腾出位置,然后将position处值置为x
T x_copy=x;
copy_backward(position,finish-2,finish-1);
*position=x_copy;
}
else //后面没备用空间,需要扩充
{
//首先需要确定之后申请空间的大小
//g++申请原则:如果原大小为0,则配置1个元素大小的空间
//如果原大小不为0,则配置2*原大小的空间
//前半段放原数据,后半段放置新数据,多余的空余
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;
try{
//1.将position之前的值拷贝过来
new_finish=uninitialized_copy(start,position,new_start);
//在position处设置新值
construct(new_finish,x);
++new_finish; //设置新finish
//将position之后的值拷贝过来
new_finish=uninitialized_copy(position,finish,new_finish);
}
catch(...){ //如果出现错误,则rollback
//因此扩容过程就像数据库中常说的 事件操作,即要么全部完成,要么全部撤回不做
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;
}
}
pop_back函数不影响vector的capacity,只是size-1。pop_back的过程就是将尾部迭代器向前移动一格,然后将其之前占据的元素和空间释放
void pop_back(){
--finish; //将尾部迭代器向前移动
destroy(finish); //释放
}
erase函数用来释放某一位置或某一区间的所有元素。
iterator erase(iterator first,iterator last)
{
iterator i=copy(last,finish,first); //将后面的元素覆盖到以first开头的位置
desrtoy(i,finish);
finish=finish-(last-first);
return first;
}
iterator erase(iterator position)
{
if(position+1!=end()) //position不在末尾
{
copy(position+1,finish,position); //将position之后的元素覆盖至position为起始的位置
}
--finish;
destroy(finish);
return position;
}
insert函数的实现处处都是细节,果然是大师!
insert函数目的是在position处插入n个x
//将n个元素x插到position位置之前。
//STL规定,区间描述时一律左闭右开(半开半闭),[start, finish)
template<class T, class Alloc>
void vector<T, Alloc>::insert(iterator position, size_type n, const T& x)
{
if(n != 0) //元素个数大于0才需要进行操作。
{
当前空间可容纳新元素
if(size_type(end_of_storage - finish) >= n)
{
T x_copy = x;
const size_type elems_after = finish - position;//计算插入点之后的元素个数。
iterator old_finish = finish; //记下原先区间内末尾元素的下一位置。
//插入点之后现有元素个数 大于新增元素个数(n)
if( elems_after > n)
{
//将原区间[finish-n, finish)填充到目标区间[finish, finish+n)。
uninitialized_copy(finish-n, finish, finish);
finish += n; //尾端标记后移。
//将源区间[position,old_finish-n)从逆向填充到目标区间[old_finish-n, old_finish)。
copy_backward(position, old_finish-n, old_finish);
//将目标区间[position, position+n)填充成x_copy。
fill(position, position+n, x_copy);
}
else //插入点之后现有元素个数 小于等于 新增元素个数(n)。
{
//将[finish, finish+(n-elems_after))全部填充成x_copy。
uninitialized_fill_n(finish, n-elems_after, x_copy);
finish += n- elems_after; //尾端标记后移。
//将原区间[position, old_finish)填到目标区间[finish, finish+(old_finish-position))
uninitialized_copy(position, old_finish, finish);
finish += elems_after; //尾端标记后移。
//将目标区间[position, old_finish)填充成x_copy。
fill(position, old_finish, x_copy);
}
}
else //因为剩余空间不足,所以需要进行扩容
{
//扩容机制:旧长度的两倍或者旧长度+新增元素个数 ,取max
const size_type old_size=size();
const size_type len=old_size+max(old_size,n);
//申请新空间
iterator new_start=data_allpcator::allocate(len);
iterator new_finish=new_start;
__STL_TRY{
//将旧的vector的position之前的元素拷贝至新空间
//新增n个新元素
//将旧的vector的position之后的元素拷贝至新空间
new_finish=uninitialized_copy(start, position, new_start);
new_finish=uninitialized_fill_n(new_finish,n,x);
new_finish=uninitialized_copy(position, old_finish, new_finish);
}
#ifdef __STL_USE_EXCEPTIONS
catch() //有异常则rollback,要么成功,要么不做
{
destroy(new_start,new_finish);
data_allocator::deallocate(new_start,len);
throw;
}
#endif
//释放原来空间
destroy(start,finish);
deallocate();
//调整迭代器位置
start=new_start;
finish=new_finish;
end_of_storage=new_start+len;
}
}
}