STL源码阅读-vector

常用的数据结构包括array、list、tree、stack、queue、map等等,array和list是其中最基本的。

vector的数据安排和操作方式和array是很相似的,唯一的区别在于vector提供了更加灵活的操作性。array是静态空间,vector可以进行动态的扩充,其本质上来说也是依赖于array,对用户而言,进行了封装,vector内部已经处理了array空间不足的种种状况。vector的实现技术,关键在于对大小的控制以及重新配置时数据移动效率,在此前提下面,很多东西就比较好理解了。

SGI对vector的定义如下,由于篇幅问题,有些类函数单独抽出来了。

template <class _Tp, class _Alloc = __STL_DEFAULT_ALLOCATOR(_Tp) >
class vector : protected _Vector_base<_Tp, _Alloc> 
{
  // requirements:

  __STL_CLASS_REQUIRES(_Tp, _Assignable);

private:
  typedef _Vector_base<_Tp, _Alloc> _Base;
public:
  typedef _Tp value_type;
  typedef value_type* pointer;
  typedef const value_type* const_pointer;
  typedef value_type* iterator;
  typedef const value_type* const_iterator;
  typedef value_type& reference;
  typedef const value_type& const_reference;
  typedef size_t size_type;
  typedef ptrdiff_t difference_type;
  typedef typename _Base::allocator_type allocator_type;
  allocator_type get_allocator() const { return _Base::get_allocator(); }

#ifdef __STL_CLASS_PARTIAL_SPECIALIZATION
  typedef reverse_iterator<const_iterator> const_reverse_iterator;
  typedef reverse_iterator<iterator> reverse_iterator;
#else /* __STL_CLASS_PARTIAL_SPECIALIZATION */
  typedef reverse_iterator<const_iterator, value_type, const_reference, 
                           difference_type>  const_reverse_iterator;
  typedef reverse_iterato<iterator, value_type, reference, difference_type>r
          reverse_iterator;
#endif /* __STL_CLASS_PARTIAL_SPECIALIZATION */

protected:
#ifdef __STL_HAS_NAMESPACES
  using _Base::_M_allocate;
  using _Base::_M_deallocate;
  using _Base::_M_start;
  using _Base::_M_finish;
  using _Base::_M_end_of_storage;
#endif /* __STL_HAS_NAMESPACES */

protected:
  void _M_insert_aux(iterator __position, const _Tp& __x);
  void _M_insert_aux(iterator __position);

public:
  iterator begin() { return _M_start; }
  const_iterator begin() const { return _M_start; }
  iterator end() { return _M_finish; }
  const_iterator end() const { return _M_finish; }

  reverse_iterator rbegin()
    { return reverse_iterator(end()); }
  const_reverse_iterator rbegin() const
    { return const_reverse_iterator(end()); }
  reverse_iterator rend()
    { return reverse_iterator(begin()); }
  const_reverse_iterator rend() const
    { return const_reverse_iterator(begin()); }

  size_type size() const
    { return size_type(end() - begin()); } 
  size_type max_size() const
    { return size_type(-1) / sizeof(_Tp); }
  size_type capacity() const
    { return size_type(_M_end_of_storage - begin()); }
  bool empty() const
    { return begin() == end(); }
}
从上面可以看到,就这一部分而言,vector的实现还是比较简单的,利用指针作为iterator,主要是因为vector是连续的内存空间,指针就可以实现iterator要求的功能。vector继承了_Vector_base,_Vector_base的定义如下:

template <class _Tp, class _Alloc> 
class _Vector_base {
public:
  typedef _Alloc allocator_type;
  allocator_type get_allocator() const { return allocator_type(); }

  _Vector_base(const _Alloc&)
    : _M_start(0), _M_finish(0), _M_end_of_storage(0) {}
  _Vector_base(size_t __n, const _Alloc&)
    : _M_start(0), _M_finish(0), _M_end_of_storage(0) 
  {
    _M_start = _M_allocate(__n);//分配内存空间,使用简单的allocate
    _M_finish = _M_start;
    _M_end_of_storage = _M_start + __n;
  }

  ~_Vector_base() { _M_deallocate(_M_start, _M_end_of_storage - _M_start); }//销毁内存空间,使用的是simple_alloc::deallocate函数实现的。

protected:
  _Tp* _M_start;
  _Tp* _M_finish;
  _Tp* _M_end_of_storage;

  typedef simple_alloc<_Tp, _Alloc> _M_data_allocator;
  _Tp* _M_allocate(size_t __n)
    { return _M_data_allocator::allocate(__n); }
  void _M_deallocate(_Tp* __p, size_t __n) 
    { _M_data_allocator::deallocate(__p, __n); }
};
vector_base里面用了三个指针,_M_start, _M_finish, _M_end_of_storage来界定vector的存储空间,vector使用连续的线性空间,就不难理解可以用指针来界定它的大小了。

_M_allocate用来分配内存空间,这两个函数的原型如下:

 typedef simple_alloc<_Tp, _Alloc> _M_data_allocator;
  _Tp* _M_allocate(size_t __n)
    { return _M_data_allocator::allocate(__n); }
  void _M_deallocate(_Tp* __p, size_t __n) 
    { _M_data_allocator::deallocate(__p, __n); }
可以看到,vector_base 或者说vector在进行内存分配的时候,采用的是simple_alloc,而不是STL自带的alloc,这个主要是vector需要的是连续的内存空间,采用simple_alloc更好。

上面vector的几个函数里面,都是比较直观的。vector进行数据初始化、删除数据和插入数据的时候,会引起内存的变化,这个时候就需要有一定的机制来调整内存大小了、

 vector的push_back原型如下:

void push_back(const _Tp& __x) {
    if (_M_finish != _M_end_of_storage) {//判断是否已经到了申请的可用内存的边界
      construct(_M_finish, __x);//直接构造函数
      ++_M_finish;
    }
    else
      _M_insert_aux(end(), __x);//已经到了内存的边界了,需要调用
  }


_M_insert_aux函数主要是负责在指定位置插入变量x,其原型为:

void 
vector<_Tp, _Alloc>::_M_insert_aux(iterator __position, const _Tp& __x)
{
  if (_M_finish != _M_end_of_storage) {//如果还有分配的空间,直接构造一下就可以了
    construct(_M_finish, *(_M_finish - 1));
    ++_M_finish;
    _Tp __x_copy = __x;
    copy_backward(__position, _M_finish - 2, _M_finish - 1);
    *__position = __x_copy;
  }
  else {
    const size_type __old_size = size();
    const size_type __len = __old_size != 0 ? 2 * __old_size : 1;//如果之前的空间大小为0,那么申请1个空间,否的话就身亲原来2倍的空间,这样就
    iterator __new_start = _M_allocate(__len);//可以避免频繁的申请内存空间和移动数据,毕竟这种开销还是挺大的。
    iterator __new_finish = __new_start;
    __STL_TRY {
      __new_finish = uninitialized_copy(_M_start, __position, __new_start);//将position之前的数据复制到新位置
      construct(__new_finish, __x);//填充新数据
      ++__new_finish;//更新起点,将后面的数据都拷贝过来
      __new_finish = uninitialized_copy(__position, _M_finish, __new_finish);
    }
    __STL_UNWIND((destroy(__new_start,__new_finish), 
                  _M_deallocate(__new_start,__len)));
    destroy(begin(), end());//调用析构函数
    _M_deallocate(_M_start, _M_end_of_storage - _M_start);//释放内存空间
    _M_start = __new_start;
    _M_finish = __new_finish;
    _M_end_of_storage = __new_start + __len;//更新内存起点地址
  }
}

按照这个申请空间的方式,我们可以知道vector 的内存大小事1,2,4,8,16,成2的指数倍增长的。从这点来看,如果vector很大的话,那么浪费的内存还是蛮多的。而且速度也会受到比较大的影响,之前做oj的时候,发现用vector跑出来的时间就会偏大。

在处理完内存之后,调用destroy进行析构,destroy函数的原型如下:

template <class _ForwardIterator>
inline void destroy(_ForwardIterator __first, _ForwardIterator __last) {
  _Destroy(__first, __last);
}
destroy会直接调用_Destroy函数

template <class _ForwardIterator>
inline void _Destroy(_ForwardIterator __first, _ForwardIterator __last) {
  __destroy(__first, __last, __VALUE_TYPE(__first));
}

_Destroy调用函数__destroy,进行类型推导。在这个推导过程中,宏定义__VALUE_TYPE是用来判断是否是基本类的。

#define __VALUE_TYPE(__i)        value_type(__i)

这个就是用来返回value_type*的指针,用于传给__destroy函数进行推导

template <class _Iter>
inline typename iterator_traits<_Iter>::value_type*
__value_type(const _Iter&)
{
  return static_cast<typename iterator_traits<_Iter>::value_type*>(0);
}

这个destroy函数就是最终的分支函数,在这里进行了T的类型推导,交给void __destroy_aux(_ForwardIterator, _ForwardIterator, __true_type)或者__destroy_aux(_ForwardIterator __first, _ForwardIterator __last, __false_type)函数处理。

template <class _ForwardIterator, class _Tp>
inline void 
__destroy(_ForwardIterator __first, _ForwardIterator __last, _Tp*)
{
  typedef typename __type_traits<_Tp>::has_trivial_destructor
          _Trivial_destructor;
  __destroy_aux(__first, __last, _Trivial_destructor());
}

这里,__destroy的第三个参数是指针,这样就清晰了,可以使用traits技术,实现类型推导。 这里用指针的好处是可以方便处理const p*、p*、p等。

通过type traits技术,最终会调用到这里进行分支

template <class _ForwardIterator>
void
__destroy_aux(_ForwardIterator __first, _ForwardIterator __last, __false_type)
{
  for ( ; __first != __last; ++__first)
    destroy(&*__first);
}

template <class _ForwardIterator> 
inline void __destroy_aux(_ForwardIterator, _ForwardIterator, __true_type) {}

可以看出来,对于基本数据类型,是不会进行任何处理的。


这样就最终实现了vector内存的动态加载,insert和push_back都是通过调用_M_insert_aux来实现数据增加的。至于pop_back等操作,就是比较简单的对指针进行--就可以了。

vector的iterato就是指针,所以在进行iterator移动处理的时候,用的是random_access的标签。

参考:

《STL源码剖析》







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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值