欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

STL之Vector源码剖析

程序员文章站 2024-02-26 21:27:28
...

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]的操作

STL之Vector源码剖析

下面是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]的操作

STL之Vector源码剖析

STL之Vector源码剖析

STL之Vector源码剖析

上一篇: asp.net发送邮件示例分享

下一篇: