STL之Vector源码剖析

STL之Vector源码剖析

vevtor与array非常相似,两者唯一差别在于空间运用的灵活性。array是静态空间,一旦配置了就不能改变;vector是动态空间,随着元素的加入,它的内部机制会自行扩充空间以容纳新元素。

vector的实现技术关键在于其对大小的控制以及重新配置时数据移动效率。一旦vector旧有空间满载,如果客户端每新增一个元素,vector内部知识扩充一个元素的空间,实为不智,因为所谓扩充空间,是”配置新空间/移动数据/释放旧空间”的大工程,时间成本很高,故应该加入某种未雨绸缪的考虑。

vector迭代器

vector维护的是一个连续线性空间。它提供的是Random Access Iterators,普通指针都可作为vector的迭代器。

template<class T, class Alloc= alloc>

classvector{

public:

   typedef T value_type;

   typedef value_type* iterator;  //vector的迭代器时普通指针

   ...

};

根据上述定义,如果客户端写出这样的代码:

vector<int>::iterator ivite;

vector<Shape>::iterator svite;

ivite的型别其实就是int*,svite的型别其实就是Shape*。

vector的数据结构

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

template<class T, class Alloc= alloc>

classvector{

   ...

protected:

   iteratorstart;       //表示目前使用空间的头

   iteratorfinish;      //表示目前使用空间的尾

   iteratorend_of_storage;//表示目前可使用空间的尾

   ...

};

可运用上面三个迭代器轻松实现以下vector的成员函数:

template<class T, class Alloc= alloc>
classvector{
   ...
public:
   iteratorbegin(){ return start;}
   iteratorend(){ return finish;}
   size_typesize() const{ returnsize_type(end() - begin());}
   size_typecapacity() const{
returnsize_type(end_of_storage - begin());}
   bool empty(){ returnbegin() == end();}
   referenceoperator[](size_type n){ return *(begin() + n);}
   referencefront(){ return *begin();}
   referenceback(){ return *end();}
   ...
};


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

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

template<class T, class Alloc= alloc>

classvector{

protected:

   typedef simple_alloc<value_type, Alloc>data_allocator;

   ...

};

vector提供了许多constructors,其中一个允许我们制定空间大小及初值:

//构造函数,允许指定vector大小n和处置value
vector<size_type n,const T& value>{ fill_initialize(n,value);}
//填充并予以初始化
voidfill_initialize(size_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)
{
   iteratorresult = data_allocator::allocate(n);
   uninitialize_fill_n(result,n, x);
   return result;
}


 

当我们以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);        
  }
 
template <class T, class Alloc>
void vector<T, Alloc>::insert_aux(iteratorposition, const T& x) {
  if (finish !=end_of_storage) {  //还有备用空间
    //在备用空间起始处构造一个元素,并以vectror最后一个元素值为其初值 
    construct(finish,*(finish - 1));
    // 调整水位
    ++finish;
  //在position后面元素向后移动,并在position插入元素
    T x_copy = x;
    copy_backward(position,finish - 2, finish - 1);
    *position = x_copy;
  }
  else {       //已无备用空间
    const size_typeold_size = size();
    const size_type len =old_size != 0 ? 2 * old_size : 1;
//以上配置原则:如果原大小为0,则配置1(个元素大小)
// 如果原大小不为0,则配置原大小的两倍
    // 前半段用来放置原数据,后半段准备用来放置新数据
    iterator new_start = data_allocator::allocate(len); // 实际配置
    iterator new_finish =new_start;
    __STL_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);
    }
 
#       ifdef  __STL_USE_EXCEPTIONS
    catch(...) {
      // "commitor rollback" 语意:若非全部成功,则一个不留
      destroy(new_start,new_finish);
      data_allocator::deallocate(new_start,len);
      throw;
    }
#       endif /*__STL_USE_EXCEPTIONS */
 
    // 析构并释放vector
    destroy(begin(),end());
    deallocate();
 
    // 调整迭代器,指向vector
    start = new_start;
    finish = new_finish;
    end_of_storage =new_start + len;
  }
}


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

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

//将尾端元素拿掉,并调整大小
void pop_back() {
    --finish;
destroy(finish);
}
//清除[first,last]中的所有元素
iterator erase(iteratorfirst, iterator last) {
    iterator i =copy(last, finish, first); //copy是全局函数
destroy(i,finish);    //全局函数
finish = finish - (last - first);
    return first;
  }
//清除某个位置上的元素
iterator erase(iterator position) {
if (position + 1 !=end()) //如果p不是指向最后一个元素
 //将p之后的元素一一向前移动
     copy(position + 1, finish, position);
 
   --finish;  // 调整水位
    destroy(finish);   // 全局函数
   return position;
  }

//清除全部元素。注意并未释放空间,以备未来可能还有加入新元素
  void clear() { erase(begin(),end()); }

下图展示了erase[first,last]的操作


下面是vector::insert的实现内容:

//从position开始,插入n个元素,元素初值为x
template <class T, class Alloc>
void vector<T, Alloc>::insert(iteratorposition, 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_typeelems_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 - elems_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_typeold_size = size();       
      const size_type len= old_size + max(old_size, n);
      // 以下配置新的 vector空间
      iterator new_start =data_allocator::allocate(len);
      iterator new_finish= new_start;
      __STL_TRY {
        // 以下首先将就vector的插入点之前的元素复制到新空间
        new_finish = uninitialized_copy(start,position, new_start);
        // 以下再将新增元素填入新空间
        new_finish = uninitialized_fill_n(new_finish,n, x);
        //以下首先将就vector的插入点之后的元素复制到新空间
       new_finish = uninitialized_copy(position,finish, new_finish);
      }
#         ifdef  __STL_USE_EXCEPTIONS
      catch(...) {
        //如果异常发生,实现 "commitor 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;
    }
  }
}

下面几个图展示了insert[position,n,x]的操作




  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值