vector的push_back操做是將一個元素插入vector的末尾。html
源碼以下:數據結構
template <class T, class Alloc = alloc> void YVector::push_back(const T& x) { if (finish != end_of_storage) { construct(finish, x); ++finish; } else { insert_aux(finish, x); } }
函數insert_aux函數
template <class T, class Alloc = alloc> void YVector::insert_aux(iterator position, const T& x) { if (finish != end_of_storage) { construct(finish, *(finish - 1)); ++finish; T copy_x = x; copy_backward(position, finish - 2, finish - 1); *position = copy_x; } else { const size_type old_size = size(); const size_type new_size = old_size == 0 ? 1 : 2 * old_size; iterator new_start = data_allocator::allocate(new_size); iterator new_finish = new_start; try { new_finish = uninitialized_copy(start, position, new_start); construct(new_finish, x); ++new_finish; new_finish = uninitialized_copy(position, finish, new_finish); } catch (...) { destroy(new_start, new_finish); data_allocator::deallocate(new_start, new_size); throw; } destroy(begin(), end()); deallocate(); start = new_start; finish = new_finish; end_of_storage = new_start + new_size; } }
須要理解以上源碼並不容易。看我一一道來。spa
1.start,finish,end_of_storage指針
首先必須瞭解vector的數據結構。如圖:code
vector是一段連續的內存空間。start,finish,end_of_storage三個指針描述了空間狀態,這三個是普通的指針。start到finish是已經使用的內存,裏面有元素。finish到end_of_storage是未使用的內存,裏面沒有元素。htm
由此三個指針能夠得出一些簡單的操做。對象
iterator begin() { return start; } //起始位置 iterator end() { return finish; } //結束位置 size_type size() const { return size_type(end() - begin()); } //已可用大小 size_type capacity() const { return size_type(end_of_storage - begin()); } //申請的內存大小 bool empty() { return begin() == end(); } //是否爲空 reference operator[](size_type n) { return *(begin() + n); } //[]操做 reference front() { return *begin(); } //首元素 reference back() { return *(end() - 1); } //尾元素
其中一些定義blog
typedef T value_type; typedef value_type* pointer; typedef value_type* iterator; typedef value_type& reference; typedef size_t size_type;
2.construct,destroy內存
這個兩個是全局的構造和析構函數。其中construct是調用placement new。
有關於placement new,參考 http://www.cnblogs.com/luxiaoxun/archive/2012/08/10/2631812.html
template<class T1, class T2> inline void construct(T1* p, const T2& value) { new (p)T1(value); }
placement new能夠簡單理解成在已經分配好的空間上構造。
在puch_back這裏的情境中,若是內存的備用區還有空間,則用x在finish指向的空間上構造,同時移動finish指針。這裏本來finish指向的空間是已經申請了的,因此使用placement new。
if (finish != end_of_storage) { construct(finish, x); ++finish; }
destroy有兩個版本。destroy實現較複雜,參看《STL源碼剖析》第二章。只需知道destroy會在指定範圍進行析構。
template <class T> inline void destroy(T* pointer) { pointer->~T(); } template <class ForwardIterator> inline void destroy(ForwardIterator first, ForwardIterator last) { //實現略 }
3.allocate,deallocate
注意到定義類時出現了Alloc,這實際上是配置器。vector默認使用alloc配置器。
template <class T, class Alloc = alloc>
在基本的alloc上封裝一層simple_alloc。以下:
template <class T, class Alloc = alloc> class simple_alloc { static T* allocate(size_t n)
{ return n == 0 ? 0 : (T*)Alloc::allocate(n * sizeof(T)); } static T* allocate()
{ return (T*)Alloc::allocate(sizeof(T)); } static void deallocate(T *p, size_t n)
{ if (n == 0) Alloc::deallocate(p, n * sizeof(T)); } static void deallocate(T *p)
{ Alloc::deallocate(p, sizeof(T)); } };
實際上內部實現就是調用malloc和free,可是會有複雜的分級配置處理。在此再也不討論。參看《STL源碼剖析》第二章。
能夠就簡單的把allocate,deallocate理解成malloc,free來輔助記憶,但務必記得沒那麼簡單。
4.uninitialized_copy,copy_backward
uninitialized_copy處理以下圖:
看起來好複雜……來看一下原型:
template <class InputIterator, class ForwardIterator> inline ForwardIterator uninitialized_copy(InputIterator first, InputIterator last, ForwardIterator result) { return __uninitialized_copy(first, last, result, value_type(result)); }
若是[result,result + (last - first))範圍內的迭代器都指向未初始化區域,則uninitialized_copy()會使用copy construct給輸入來源[first,last)範圍內的每個對象產生一個拷貝放進輸出範圍。
更深的實現也貼出來:
template <class InputIterator, class ForwardIterator> inline ForwardIterator __uninitialized_copy(InputIterator first, InputIterator last, ForwardIterator result, T*) { typedef typename __type_traist<T>::is_POD_type is_POD_type is_POD; return __uninitialized_copy_aux(first, last, result, is_POD()); } template <class InputIterator, class ForwardIterator> inline ForwardIterator __uninitialized_copy(InputIterator first, InputIterator last, ForwardIterator result, __true_type) { return copy(first, last, result); } template <class InputIterator, class ForwardIterator> inline ForwardIterator __uninitialized_copy(InputIterator first, InputIterator last, ForwardIterator result, __false_type) { ForwardIterator cur = result; for (; last != first; first++, cur++) { construct(&*cur, *first); } return cur; }
這裏面的邏輯是,首先把result的value_type得出,判斷是不是POD類型……
若是是POD類型,則調用copy函數,若是不是POD類型,則一個一個調用construct()
所謂POD類型,指的是擁有無心義的構造、析構、拷貝、賦值函數的類型…能不能理解成比較簡單的類。
像是若是類成員裏有一個其餘類的指針,這種複雜的類,須要有特殊的構造函數,就沒有默認的那個構造函數。所以是non-POD類型。
接下來回到push_back。insert_aux裏面判斷還有備用空間的地方,有一個copy_backword函數。來看一下實現:
template<class BidirectionalIterator1, class BidirectionalIterator2> BidirectionalIterator2 copy_backward ( BidirectionalIterator1 first, BidirectionalIterator1 last, BidirectionalIterator2 result ) { while (last!=first) *(--result) = *(--last); return result; }
做用是將一個範圍中的元素按逆序拷貝到新的位置處。insert_aux截取以下:
if (finish != end_of_storage) { construct(finish, *(finish - 1)); ++finish; T copy_x = x; copy_backward(position, finish - 2, finish - 1); *position = copy_x; }
這裏面有兩個問題。第一爲何要有一份拷貝T copy_x = x;問題的答案參考知乎 https://www.zhihu.com/question/56911557/answer/150928396
第二個 copy_backward(position,finish - 2,finish - 1) 在插入位置是末尾的時候不會死循環嗎?黑人問號?
position實際上是finish - 1,也就是說傳入參數後first比last還後面,那豈不是死循環?
引用:
1.《STL源碼剖析》
2.http://www.cnblogs.com/luxiaoxun/archive/2012/08/10/2631812.html