《STL源码剖析》之vector分析

vector作为实际项目用的非常频繁的容器,你了解它的内部实现吗?

比如vector<int>::itertor iter

1、iter到底是什么呢?

2、push_back到底发生什么了?

3、erase或者clear的时候内存有没有被释放?

如果这些问题你已经知道,可以不用看这篇文章了。

本文参考的版本是SGI for GCC。

大家都知道STL是泛型编程的典范,vector当然也是,有码有真相:

template <class _Tp, class _Alloc = __STL_DEFAULT_ALLOCATOR(_Tp) >
class vector : protected _Vector_base<_Tp, _Alloc> 
{
//此处省略N多字
}

可以看出_Tp是value_type,而后面还有默认末班类型_Alloc,就是之前分析的内存池,如果不知道,请翻阅jjhou的书,或者参考 STL对空间配置的设计哲学-《STL源码剖析笔记》

第一个问题,很简单,从源码就可以看到:

  typedef const value_type* const_pointer;
  typedef value_type* iterator;
  typedef const value_type* const_iterator;

原来iterator就是value_type*啊,比如声明了vector<int>::itertor iter,这个iter其实就是int*。

2、push_back发生了什么呢?

当然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_finish与_M_end_of_storage指的是神马嘛?_M_finish是当前元素的最后位置,_M_end_of_storage是当前可用内存的最大处。

如果当前还有可用内存,那就直接用placement new构造元素(不知道的google placement new,其实很简单就是在给定内存上构造元素);

否则,就_M_insert_aux,这个函数体有点小复杂,但是是重点(插一句:古语有言:莫将容易得,却坐等闲看。):

记住我们要往最后一个实际当前无效的内存上去插入元素,因为end()肯定是无效啦。

好,现在看下代码注释版:

template <class _Tp, class _Alloc>
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));	//placement new一个出来 
    ++_M_finish;
    _Tp __x_copy = __x;
    copy_backward(__position, _M_finish - 2, _M_finish - 1); //增大__position
    *__position = __x_copy;//拷贝值上去 
  }
  else {
    const size_type __old_size = size();	//获得当前内存量  
    //这个有个东东要注意,开始插入第一个元素时,size为1;之后都是2倍扩张,oh!vector内存扩展的秘密就在这里 
    const size_type __len = __old_size != 0 ? 2 * __old_size : 1; 
    iterator __new_start = _M_allocate(__len);//其实就是malloc分配 ,当然内部分配失败会handle的,这里不需要管 
    iterator __new_finish = __new_start;
    __STL_TRY {//try的意思 
    	// 下面就是内存从(_M_start, __position)的值复制到(__new_start, __new_start+(__position-_M_start))
    	// 谁让老家地方不够,现在可好, 要全部转移到高大上的新家了 
      __new_finish = uninitialized_copy(_M_start, __position, __new_start);
      // 此时再在新家的下一个可用位置放下新成员__X 
      construct(__new_finish, __x);
      // 更新以下各可用位置 
      ++__new_finish;
      // 这一步怎么来的,因为可能 (__position, _M_finish)之间还有成员,所以一并挪到新家 
      __new_finish = uninitialized_copy(__position, _M_finish, __new_finish);
    }
    // 这里是如果try失败,就回滚,释放分配的内存空间 
    __STL_UNWIND((destroy(__new_start,__new_finish), 
                  _M_deallocate(__new_start,__len)));
    // 搬进新家,老家就拆了吧,可用继续用作其他开发(比如XXX) 
    destroy(begin(), end());
    _M_deallocate(_M_start, _M_end_of_storage - _M_start);
    // 更新当前的start,finish,storage 
    _M_start = __new_start;
    _M_finish = __new_finish;
    _M_end_of_storage = __new_start + __len;
  }
}

嗯嗯,就是酱紫,第二个问题搞明白了吧?如果没有,请打开jjhou的书狂看几遍。

3、erase不就是移除元素吗,要知道作为序列式容器 随机移除是O(N)的复杂度,因为要移位,这个原理大家应该都知道,但是被释放的内存哪里去了?

  iterator erase(iterator __position) {
    if (__position + 1 != end())//如果不是最后一个元素 
    //就把 (__position + 1, _M_finish)上的元素挪到 (__position, (_M_finish-__position + 1))上,
	// 其实效率更高的是,memmove,STL内部当然很很聪明啦,如果型别是POD就会直接memmove,否则才酱紫挪 
      copy(__position + 1, _M_finish, __position);
    // 更新finish位置 
    --_M_finish;
    // 释放 _M_finish到哪里呢? 前面其实已经叙述过,如果从内存池分配的就还给内存池;否则才还给OS 
    destroy(_M_finish);
    return __position;
  }

clear就是重复调用了erase,原理是类似的。

好了,自此三个问题回答完毕,有木有很简单。偷笑



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值