您的位置:首页 > 其它

vector-STL源码及用法详解(源码面前了无秘密)

2017-07-16 23:01 337 查看
vector属于序列式容器。

在C++中属于序列式容器的包含以下这些:

array(C++内建)

vector

heap(以算法形式呈现xxx_heap)

priority-queue

list

slist

deque

stack(配接器)

queue(配接器)

在C++中属于关联式容器的包含以下这些:

RB-tree

set

map

multiset

multimap

hashtable

hash_set

hash_map

hash_multiset

hash_multimap

vector的数据安排以及操作方式,与array非常相似,两者的唯一差别就是空间的运用的灵活性。array是静态空间,一旦配置就不能改变。vector是动态空间,随着元素的加入,它的内部机制会自行扩充以容纳新元素。

1.下面我们来看看vector定义的源代码摘录:

// alloc 是SGI STL的空间配置器
template<class T,class Alloc=alloc>
class vector{
public:
//vector的嵌套型别定义
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:
//simple_alloc 是SGI STL的空间配置器
typedef simple_alloc<value_type,Alloc> data_allocator;
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=finsih;
}

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);}

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(){
destroy(start,finish);
deallocate();
}

reference front(){return *begin();}//第一个元素
reference back() {return *(end()-1);}//最后一个元素
void push_back(const T& x){//将元素插入至最尾端
if(finish!=end_of_storage){
construct(finish,x);
++finish;
}
else
insert_aux(end(),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,const T& x)
{
if(new_size<size())
erase(begin()+new_size,end());
else
insert(end(),new_size-size(),x);
}
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);
uninitialized_fill_n(result,n,x);
return result;
}
};


2.vector的迭代器:

template<class T,class Alloc=alloc>
class vector{
public:
typedef     T             value_type;
typedef     value_type*   iterator;//vector的迭代器是普通指针
};


因此,如果客户端写出这样的代码:

vector<int>::iterator ivite;
vector<Shape>::iterator svite;


ivite的型别就是int*,svitede 的型别就是Shape*

3.vector的数据结构:

vector采用的数据结构非常简单:线性连续空间。它以两个迭代器start和finish分别指向配置得来的连续空间中目前已被使用的范围,并以end_of_storage指向整块连续空间(含备用空间)的尾端:

template<class T,class Alloc=alloc>
class vector{
...
protected:
iterator start;
iterator finish;
iterator end_of_storage;
};


为了降低空间配置时的速度成本,vector实际配置大小可能比客户端需求量更大一些,以备将来可能的扩充。这就是容量(capacity)的观念。即一个vector的容量永远大于或等于其大小。一旦容量等于大小,便是满载,下次再有新增元素,整个vector就得另觅居所。

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);}
...
};


4.vector的构造与内存管理:constructor,push_back

下面通过一个程序来观察vector构造的方式、元素的添加,以及大小容量的变化

//filename:4vector-test.cpp
#include<vector>
#include<iostream>
#include<algorithm>
using namespace std;

int main()
{
int i;
vector<int> iv(2,9);
cout<<"size"<<iv.size()<<endl;                                //size=2
cout<<"capacity="<<iv.capacity()<<endl;                       //capacity=2

iv.push_back(1);
cout<<"size="<<iv.size()<<endl;                               //size=3
//增加新元素(s)时,如果超过当时的容量,则容量会扩充至两倍。如果两倍容量还不足,就扩张至足够大的容量
cout<<"capacity="<<iv.capacity()<<endl;                       //capacity=4(扩张至两倍)

iv.push_back(2);
cout<<"size="<<iv.size()<<endl;                               //size=4
cout<<"capacity="<<iv.capacity()<<endl;                       //capacity=4

iv.push_back(3);
cout<<"size="<<iv.size()<<endl;                               //size=5
cout<<"capacity="<<iv.capacity()<<endl;                       //capacity=8

iv.push_back(4);
cout<<"size="<<iv.size()<<endl;                               //size=6
cout<<"capacity="<<iv.capacity()<<endl;                       //capacity=8

for(i=0;i<iv.size();i++)
{
cout<<iv[i]<<' ';                                          // 9 9 1 2 3 4
}
cout<<endl;

iv.push_back(5);
cout<<"size="<<iv.size()<<endl;                               //size=7
cout<<"capacity="<<iv.capacity()<<endl;                       //capacity=8
for(i=0;i<iv.size();i++)
{
cout<<iv[i]<<' ';                                          //9 9 1 2 3 4 5
}
cout<<endl;

iv.pop_back();
iv.pop_back();
cout<<"size="<<iv.size()<<endl;                               //size=5
cout<<"capacity="<<iv.capacity()<<endl;                       //capacity=8

iv.pop_back();
cout<<"size="<<iv.size()<<endl;                               //size=4
cout<<"capacity="<<iv.capacity()<<endl;                       //capacity=8

vector<int>::iterator ivite=find(iv.begin(),iv.end(),1);
if(ivite!=iv.end())
{
iv.erase(ivite);
}
cout<<"size="<<iv.size()<<endl;                               //size=3;
cout<<"capacity="<<iv.capacity()<<endl;                       //capacity=8
for(i=0;i<iv.size();i++)
{
cout<<iv[i]<<' ';                                          // 9 9 2
}
cout<<endl;

ivite=find(iv.begin(),iv.end(),2);
if(ivite!=iv.end())
{
iv.insert(ivite,3,7);
}
cout<<"size="<<iv.size()<<endl;                               //size=6;
cout<<"capacity="<<iv.capacity()<<endl;                       //capacity=8
for(i=0;i<iv.size();i++)
{
cout<<iv[i]<<' ';                                          //9 9 7 7 7 2
}
cout<<endl;

iv.clear();
cout<<"size="<<iv.size()<<endl;                               //size=0;
cout<<"capacity="<<iv.capacity()<<endl;                       //capacity=8
}


vector缺省参数使用alloc作为空间配置器,并据此另外定义了一个data_allocator,为的是更方便以元素大小为配置单位:

template <class T,class Alloc=alloc>
class vector{
protected:
typedef simple_alloc<value_type,Alloc> data_allocator;
...
};


data_allocator::allocate(n)表示配置n个元素空间

vector提供很多构造函数,其中一个允许我们制定空间大小以及初值:

//构造函数,允许指定vector大小n和初值value
vector(size_type n,const T& value) {fill_initialize(n,value);}

//填充并予以初始化
void fill_initialize(suze_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);//配置n个元素空间
uninitialized_fill_n(result,n,x);
return result;
}


注意:uninitialized_fill_n()会根据第一参数的型别特性决定使用算法fill_n()或反复调用constructor()来完成任务。

当我们以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);//vector member function
}
}


重点来了:

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*ols_size:1;
//以上原则,如果原大小为0,则配置1(个元素)
//如果原大小不为0,则配置原大小的两倍
//前半段用来放置元数据,后半段用来放置新数据

iterator new_start=data_allocator::allocate(len);//实际配置
iterator new_finish=new_start;
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);
}
catch(...){
destroy(new_start,new_finish);
data_allocator::deallocate(new_start,len);
throw;
}

//析构并释放原vector
destory(begin(),end());
deallocate();

//调整迭代器,指向新vector
start=new_start;
finish=new_finish;
end_of_storage=new_start+len;
}

}


注意,所谓动态增加大小,并不是在原空间之后接续新空间(因为无法保证原空间之后尚有可供配置的空间),而是以原大小的两倍另外配置一块较大空间,然后将原内容拷贝过来,然后才开始在原内容之后构造新元素,并释放原空间。因此,对vector的任何操作,一旦引起空间重新配置,指向原vector的所有迭代器就都失效了。

5.vector的元素操作:pop_back,erase,clear,insert

//将尾端元素拿掉,并调整大小
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;
}

//清除某个位置上的元素
iterator erase(iterator position)
{
if(position+1!=end()){
}
--finish;
destroy(finish);
return position;
}

void clear() {erase(begin(),end());}

//下面是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;//将vector尾端标记后移
copy_backward(position,old_finish-n,old_finish);
fill(position,position+n,x_copy);//从插入点开始填入新值
}
else{
//“插入点之后的现有元素个数”小于等于“新增元素个数”
uninitialized_fill_n(finish,n-eles_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(n);
iterator new_finish=new_start;
__STL_TRY{
//以下首先将旧vector的插入点之前的元素复制到新空间
new_finish=uninitialized_copy(start,position,new_start);
//以下再将新增元素(初值皆为n)填入新空间
new_finish=uninitialized_fill_n(new_finish,n,x);
//以下再将旧vector的插入点之后的元素复制到新空间
new_finish=uninitialized_copy(position,finish,new_finish);
}
#ifdef  __STL_USE_EXCEPTIONS
catch(...){
//如有异常发生,实现“commit or 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;
}
}
}


6.vector用法实例:

1.基本操作:

(1)创建vector对象:vector ivec;

(2)尾部插入数字:ivec.push_back(n);

(3)使用下标访问元素:ivec[i];

(4)使用迭代器访问元素:

vector<int>::iterator iv;
for(iv=ivec.begin();iv!=ivec.end();iv++)
{
cout<<*iv<<endl;
}


(5)插入元素:ivec.insert(ivec.begin()+i,a);//在第i+1个元素前面插入a

(6)删除元素:ivec.erase(ivec.begin()+1);//删除第二个元素

ivec.erase(ivec.begin()+i,ivec.begin()+j);//删除[i,j-1]区间

(7)向量大小:ivec.size();

(8)清空:ivec.clear();

vector的元素不仅仅可以是Int,double,string,甚至还可以是结构体,但是要注意,结构体必须定义成全局的,否则会出错。下面是一段代码示例:

代码参考地址:http://www.cnblogs.com/wang7/archive/2012/04/27/2474138.html

#include<stdio.h>
#include<algorithm>
#include<vector>
#include<iostream>

using namespace std;

typedef struct rect{
int id;
int length;
int width;

//对于向量元素是结构体的,可在结构体内部定义比较函数,下面按照id,length,width升序排序
bool operator< (const rect &a) const
{
if(id!=a.id)
return id<a.id;
else
{
if(length!=a.length)
return length<a.length;
else
return width<a.width;
}
}
}Rect;

int main()
{
vector<Rect> ivec;
Rect rect;
rect.id=1;
rect.length=2;
rect.width=3;
ivec.push_back(rect);
vector<Rect>::iterator it=ivec.begin();
cout<<(*it).id<<' '<<(*it).length<<' '<<(*it).width<<endl;
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: