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

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/sinat_33442459/article/details/75142672

vector属于序列式容器。

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

  1. array(C++内建)
  2. vector
  3. heap(以算法形式呈现xxx_heap)
  4. priority-queue
  5. list
  6. slist
  7. deque
  8. stack(配接器)
  9. queue(配接器)

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

  1. RB-tree
  2. set
  3. map
  4. multiset
  5. multimap
  6. hashtable
  7. hash_set
  8. hash_map
  9. hash_multiset
  10. 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;
}

猜你喜欢

转载自blog.csdn.net/sinat_33442459/article/details/75142672