vector-STL源码及用法详解(源码面前了无秘密)
vector属于序列式容器。在C++中属于序列式容器的包含以下这些:array(C++内建)vectorheap(以算法形式呈现xxx_heap)priority-queuelistslistdequestack(配接器)queue(配接器)在C++中属于关联式容器的包含以下这些:RB-treesetmapmultisetmultimaphashtablehash_
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;
}
更多推荐
所有评论(0)