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定义的源代码摘录:
2.vector的迭代器:
因此,如果客户端写出这样的代码:
ivite的型别就是int*,svitede 的型别就是Shape*
3.vector的数据结构:
vector采用的数据结构非常简单:线性连续空间。它以两个迭代器start和finish分别指向配置得来的连续空间中目前已被使用的范围,并以end_of_storage指向整块连续空间(含备用空间)的尾端:
为了降低空间配置时的速度成本,vector实际配置大小可能比客户端需求量更大一些,以备将来可能的扩充。这就是容量(capacity)的观念。即一个vector的容量永远大于或等于其大小。一旦容量等于大小,便是满载,下次再有新增元素,整个vector就得另觅居所。
4.vector的构造与内存管理:constructor,push_back
下面通过一个程序来观察vector构造的方式、元素的添加,以及大小容量的变化
vector缺省参数使用alloc作为空间配置器,并据此另外定义了一个data_allocator,为的是更方便以元素大小为配置单位:
data_allocator::allocate(n)表示配置n个元素空间
vector提供很多构造函数,其中一个允许我们制定空间大小以及初值:
注意:uninitialized_fill_n()会根据第一参数的型别特性决定使用算法fill_n()或反复调用constructor()来完成任务。
当我们以push_back()将新元素插入于vector尾端时,该函数首先检查是否还有备用空间,如果有就直接在备用空间上构造函数,并调整迭代器finish,使vector变大。如果没有备用空间,就扩充空间(重新配置,移动数据,释放原空间)
重点来了:
注意,所谓动态增加大小,并不是在原空间之后接续新空间(因为无法保证原空间之后尚有可供配置的空间),而是以原大小的两倍另外配置一块较大空间,然后将原内容拷贝过来,然后才开始在原内容之后构造新元素,并释放原空间。因此,对vector的任何操作,一旦引起空间重新配置,指向原vector的所有迭代器就都失效了。
5.vector的元素操作:pop_back,erase,clear,insert
6.vector用法实例:
1.基本操作:
(1)创建vector对象:vector ivec;
(2)尾部插入数字:ivec.push_back(n);
(3)使用下标访问元素:ivec[i];
(4)使用迭代器访问元素:
(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
在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; }
相关文章推荐
- C++模版STL中 map 和 string, vector 的用法详解!
- STL之二:vector容器用法详解
- STL之一:vector用法详解
- STL之二:vector容器用法详解
- [转载]STL:vector容器用法详解
- C++学习 STL之二:vector容器用法详解
- STL:vector容器用法详解
- C++ STL--vector容器用法详解
- STL中map和string, vector 用法详解
- STL 中 map 和 string, vector 的用法详解
- STL — vector容器用法详解
- STL中map和string, vector 用法详解
- STL之二:vector容器用法详解
- STL 中 map 和 string, vector 的用法详解
- STL中map和string, vector 用法详解
- STL中map和string, vector 用法详解
- C++模版STL中 map 和 string, vector 的用法详解!
- 【用法详解】C++--STL中vector的用法总结
- STL中map和string, vector 用法详解
- C++模版STL中 map 和 string, vector 的用法详解!