上篇文章中很粗略的角度讲解了一下stl_deque的设计思想,以及涉及到得浅显的STL内存管理方面,至少我们看得到的冰山一角.
这篇文章中关于vector的分析,我将将一些问题细化一下,对一些函数做细致的分析.有些时候,有些问题还是说清楚比较好.
打开stl_vector的源码,发现vector的设计思路和stl_deque如出一辙,想想这样是很合理的,保持实现的一致性.只是stl_vector没有提供一个确定的
模板类Iterator去实现迭代器,而是在vector模板类中实现了迭代. 但是和deque基本没有什么大的不同.下面来看看源码:
1. 这次就不讲解设计部分了,主要讲解实现部分.如果没有看前面一篇文章,我尽力做到和前一篇文章没有关系,但是最好还是去了解一下设计部分.详细
可参考这篇文章:C++ Standard Stl -- SGI STL源码学习笔记(04) stl_deque && 初涉STL内存管理.
2. vector的内存管理模板类:_Vector_alloc_base. 它是一个模板类,但是有一个特化的版本,区别在于对分配器Allocator(不知道这种分配器的叫法
是否合适,以前阅读apache源码分析的时候,里面对于allocator称作分配子,但是这两者的作用和概念都是不同的.)对象是否为成员变量.
好吧,对于通用_Vector_alloc_base模板类和特化_Vector_alloc_base模板类,我们从内存操作的方法上面区分一下:
通用_Vector_alloc_base模板内存操作方法:
_Tp* _M_allocate(size_t __n) // 使用成员变量实现内存操作 { return _M_data_allocator.allocate(__n); } void _M_deallocate(_Tp* __p, size_t __n) { if (__p) _M_data_allocator.deallocate(__p, __n); }
特化_Vector_alloc_base内存操作方法:
typedef typename _Alloc_traits<_Tp, _Allocator>::_Alloc_type _Alloc_type; // 使用方法直接操作,没有作为成员变量 _Tp* _M_allocate(size_t __n) { return _Alloc_type::allocate(__n); } void _M_deallocate(_Tp* __p, size_t __n) { _Alloc_type::deallocate(__p, __n);}
当然,无论是通用模板类还是特化模板类,都使用了traits技术.
3. vector的成员变量:
_Tp* _M_start; // start _Tp* _M_finish; // 初始化的时候_M_finish = _M_start _Tp* _M_end_of_storage // 指向最后一个元素的下一个位置. 注意不是最后一个元素.
这三个成员变量都是vector继承自基类, 这三个指针指向三个不同的位置. 可以从构造函数中看得到:
_Vector_base(size_t __n, const _Alloc&) : _M_start(0), _M_finish(0), _M_end_of_storage(0) { _M_start = _M_allocate(__n); _M_finish = _M_start; _M_end_of_storage = _M_start + __n; }
根据初始化的长度_n初始化_M_start, _M_finish, _M_end_of_storage.
explicit vector(const allocator_type& __a = allocator_type()) : _Base(__a) {} vector(size_type __n, const _Tp& __value, const allocator_type& __a = allocator_type()) : _Base(__n, __a) { _M_finish = uninitialized_fill_n(_M_start, __n, __value); } explicit vector(size_type __n) : _Base(__n, allocator_type()) { _M_finish = uninitialized_fill_n(_M_start, __n, _Tp()); } vector(const vector<_Tp, _Alloc>& __x) : _Base(__x.size(), __x.get_allocator()) { _M_finish = uninitialized_copy(__x.begin(), __x.end(), _M_start); }
由vector提供的构造函数可以看得出,都是使用基类_vector_base的构造函数进行初始化.由于vector模板类提供了一个默认参数,可以使用默认
分配器allocator.
inline T* allocate(ptrdiff_t size, T*) { set_new_handler(0); T* tmp = (T*)(::operator new((size_t)(size * sizeof(T)))); if (tmp == 0) { cerr << "out of memory" << endl; exit(1); } return tmp; }
初始化后M_start得到了allocate函数的返回指针,而M_finish默认初始化和M_start相同.看看对于_M_finish的处理.
由宏uninitialized_fill_n处理,进行跳转,到__uninitialized_fill_n,再到__uninitialized_fill_n_aux.
SGI STL在这里做了相当复杂的处理,主要是判断vector里面的元素类型判断,是POD类型还是不是POD类型.(POD? Plain Old Data,朴素的旧式数据,built-in类型就是POD
可以参开<<c++必知必会>> 条款11 编译期会在类中放东西. 其中讲到了类多态中的v-table,考虑是不是写一篇随笔介绍一下.)
template <class _ForwardIter, class _Size, class _Tp, class _Tp1> inline _ForwardIter __uninitialized_fill_n(_ForwardIter __first, _Size __n, const _Tp& __x, _Tp1*) { typedef typename __type_traits<_Tp1>::is_POD_type _Is_POD; return __uninitialized_fill_n_aux(__first, __n, __x, _Is_POD()); }
_type_traits模板类提供了很多偏特化,都是处理这种build-in类型. 对于build-in POD is_POD_type都是_true_type.
template <class _ForwardIter, class _Size, class _Tp> inline _ForwardIter __uninitialized_fill_n_aux(_ForwardIter __first, _Size __n, const _Tp& __x, __true_type) { return fill_n(__first, __n, __x); } template <class _ForwardIter, class _Size, class _Tp> _ForwardIter __uninitialized_fill_n_aux(_ForwardIter __first, _Size __n, const _Tp& __x, __false_type) { _ForwardIter __cur = __first; __STL_TRY { for ( ; __n > 0; --__n, ++__cur) _Construct(&*__cur, __x); return __cur; } __STL_UNWIND(_Destroy(__first, __cur)); }
这里是对_M_finish的最终实现.补充fill_n的源码:
template <class _OutputIter, class _Size, class _Tp> _OutputIter fill_n(_OutputIter __first, _Size __n, const _Tp& __value) { __STL_REQUIRES(_OutputIter, _OutputIterator); for ( ; __n > 0; --__n, ++__first) *__first = __value; return __first; }
不管vector中的元素类型是否是POD,都是移动_M_start,而且还做了相应的初始化.