1. 程式人生 > >STL原始碼分析之vector(二)—核心函式 push_back及insert_aux

STL原始碼分析之vector(二)—核心函式 push_back及insert_aux

說明: 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)
的分析!
(未完待續)