STL源码分析之vector(二)—核心函数 push_back及insert_aux

来源:互联网 发布:数据库约束是什么意思 编辑:程序博客网 时间:2024/05/30 23:10

说明: STL源码分析系列博客的使用的是https://www.sgi.com/tech/stl/download.html 里面的STL v2.03版.不同的STL或许会有所不同。
其它vector内容请参照本系列其它博客。

主要函数分析

1.迭代器相关:

都是返回指针:

1.1

iterator begin() //返回初始位置指针{ return start; }const_iterator begin() const { return start; }

1.2

iterator end() { return finish; }//返回结束位置的指针!注意finish不指向任何有效的元素,具体可参照上一篇博客vector(1)所讲。const_iterator end() const { return finish; }

1.3

reverse_iterator rbegin() { return reverse_iterator(end()); }//反向迭代器

其定义为:

    typedef reverse_iterator<iterator, value_type, reference, difference_type>reverse_iterator;

其实是再一次封装的迭代器。就是把最后end()给返回了!

   const_reverse_iterator rbegin() const    {         return const_reverse_iterator(end()); }

1.4

reverse_iterator rend() { return reverse_iterator(begin()); }与rbegin一致,把begin()返回。    const_reverse_iterator rend() const {         return const_reverse_iterator(begin()); }

2.插入相关:

2.1 insert_aux()函数,

这个是protected函数,是其它插入函数的基础!
具体看代码!

template <class T, class Alloc>void vector<T, Alloc>::insert_aux(iterator position, const T& x) {  if (finish != end_of_storage) {    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 * old_size : 1;    iterator new_start = data_allocator::allocate(len);    iterator new_finish = new_start;#       ifdef __STL_USE_EXCEPTIONS    try {#       endif /* __STL_USE_EXCEPTIONS */      new_finish = uninitialized_copy(start, position, new_start);      construct(new_finish, x);      ++new_finish;      new_finish = uninitialized_copy(position, finish, new_finish);#       ifdef __STL_USE_EXCEPTIONS    }    catch(...) {      destroy(new_start, new_finish);      data_allocator::deallocate(new_start, len);      throw;    }#       endif /* __STL_USE_EXCEPTIONS */    destroy(begin(), end());    deallocate();    start = new_start;    finish = new_finish;    end_of_storage = new_start + len;  }}

第3行 先判断finish是否等于end_of_storage!,finish指向最后一个元素的末尾,而end_of_storage指向动态内存空间的末尾,两者不一定会相等!
我们先进入不相等的流程:
第5行:construct(finish, *(finish - 1));
我们来看construct函数:

template <class T1, class T2>inline void construct(T1* p, const T2& value) {    new (p) T1(value);}

new (p) T1(value);是一个placement new的用法,new的这个用法是在一个已分配好内存的地方调用构造函数来初始化一下。

p是等于finish的,而value等于*(finish-1);注意finish指向最后一个有效元素的后一个元素,因此finish所指的元素是尚未初始化好的!

new (p) T1(value);的含义就是:
new (finish) T1(*(finish-1));用最后一个有效元素的值来初始化finish所指的空间。

第6 行:++finish;让finish后移一个元素,因为经过第5行,vector里面已经多了一个有效元素啦!

第7行:T x_copy=x;生成一个临时对象 x_copy

第8行:copy_backward(position, finish - 2, finish - 1);

我们看copy_backward()在搞什么鬼:
···

template <class T>inline T* __copy_backward_t(const T* first, const T* last, T* result,                            __true_type) {  const ptrdiff_t N = last - first;  memmove(result - N, first, sizeof(T) * N);  return result - N;}

···
核心是memmove()

memmove() 用来复制内存内容,其原型为:

    void * memmove(void *dest, const void *src, size_t num);

memmove() 与 memcpy() 类似都是用来复制 src 所指的内存内容前 num 个字节到 dest 所指的地址上。不同的是,memmove() 更为灵活,当src 和 dest 所指的内存区域重叠时,memmove() 仍然可以正确的处理。

参数带入就是:first=position,last=finish-2,result=finish-1

const ptrdiff_t N = last - first;
memmove(result - N, first, sizeof(T) * N);

其实就是:memmove(1+position,position,sizeof(T)*(finish-2-position));

这为postion腾出一个元素的位置,postion后面的元素都向后移。

第9 行: *position = x_copy; 把x_copy内容拷贝过去。


好 我们现在来看else的流程了!
else是在finish==end_of_storage的时候,这个时候 之前分配的内存全部使用完了,需要重新分配内存!那么怎么分配呢?
第12 行:old_size=size();保存旧的元素个数。

第13 行: len=old_size!=0 ? 2* old_size:1;
如果原来就没有任何有效元素,则len=1,否则len是旧元素个数的两倍!

第14行: iterator new_start = data_allocator::allocate(len);
申请一块新的可供len个元素使用的动态内存!

第19行:new_finish = uninitialized_copy(start, position, new_start);

inline wchar_t* uninitialized_copy(const wchar_t* first, const wchar_t* last,wchar_t* result) {  memmove(result, first, sizeof(wchar_t) * (last - first));  return result + (last - first);}

把start到postion位置的全部内存单元都拷贝到以new_start开始的地方。
同时返回new_finish=new_start+(position-start)

第20 行:construct(new_finish,x); 把x的值拷贝到new_finish那里去。

第21 行:++new_finish;new_finish指向目前最后一个有效元素的后面

第22 行:new_finish = uninitialized_copy(position, finish, new_finish);
把postion~finish的内存部分拷贝到new_finish去。其实就是先后偏移,给position赋值留出一个空位。

第31 行:destroy(begin(), end());
从begin()到end()调用这些元素的析构函数!

template <class ForwardIterator>inline void destroy(ForwardIterator first, ForwardIterator last) {  __destroy(first, last, value_type(first));}template <class ForwardIterator>inline void__destroy_aux(ForwardIterator first, ForwardIterator last, __false_type) {  for ( ; first < last; ++first)    destroy(&*first);}template <class T>inline void destroy(T* pointer) {    pointer->~T();}

第32行:deallocate();

 void deallocate()  {      if (start) data_allocator::deallocate(start, end_of_storage - start); }

回收内存!

第33 行:start = new_start;
第34 行:finish = new_finish;
第35 行: end_of_storage = new_start + len; 将end_of_storage指向新申请的内存的尾部,注意end_of_storage>= finish!

总结:
insert_aux(position,const T& value)的过程:
1. 判断当前的动态内存是否全部使用完毕,是进入3,否则进入2.
2.
2.1 首先将finish所指的元素用最后一个有效元素初始化.
2.2 将finish后移一个单位
2.3 在动态内存区别给Position处的元素腾出一个位置。
2.4 将value的值赋值给position.
3.
3.1 先记录原来的有效元素个数old_size
3.2 如果old_size为0,则len=1,否则len=old_size*2
3.3 申请原来old_size两倍的动态内存,新内存的起始位置为new_start
3.4 将原来start到position的内存的内容拷贝到new_start开始的内存区域。
3.5 将value的值存入position位置.
3.6 将原来position到finish的元素拷贝到新内存块中position位置的后面。
3.4-3.6 就是为保存value而腾出了一个位置
3.7 调用原来内存上所有元素的析构函数
3.8 回收原来的动态内存
3.9 更新finish,start,end_of_storage.其中end_of_storage指向新申请内存的尾部,finish指向所有有效元素的后一个元素。

2.2 push_back(const T & x)

这个是相当常用的函数!我们看它是如何实现的。

void push_back(const T& x) {    if (finish != end_of_storage) {        construct(finish, x);        ++finish;    } else        insert_aux(end(), x);    }

先判断原来的申请的内存是否支持再添加一个元素,如果支持就把x赋值到finish去,同时让finish自增。否则 调用Insert_aux(end(),x);这个时候根据上面的分析,系统会新申请原来元素2倍的内存,然后再插入元素,具体细节见博客上面template <class T, class Alloc>
void vector<T, Alloc>::insert_aux(iterator position, const T& x)
的分析!
(未完待续)