上篇文章中很粗略的角度講解了一下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,而且還做了相應的初始化.