STL淺析——序列式容器vector的構造和內存管理: constructor() 和 push_back()
阿新 • • 發佈:2017-05-09
技術 eal ace name des 分享 names private rst
咱們先來做一個測試capacity是容器容量,size是大小:
#include <iostream> #include <vector> using namespace std; int main(){ vector<int> result; for (int i = 0; i < 17; i++) { result.push_back(i); printf("element count: %d\t", result.size()); printf("capacity: %d\t", result.capacity()); printf("first element‘s address: %x\n", result.begin()); } return 0; }
運行結果:
可以觀察到每次容器滿了需要擴容的時候,容量總是呈現兩倍增長,而且每次擴容,容器第一個元素所在地址都會發生改變,由此我們知道,容器的擴容時實際是另外尋找一片更大的空間,VS的如下:
擴容的倍數不一樣VS為1.5倍擴容,最好的擴容倍數是黃金分割比,1.618倍,當然也不可能那麽精確。。。
vector 缺省使用 alloc (實際上是 宏__STL_DEFAULT_ALLOCATOR(_Tp) 的typedef,但是該宏本質還是第二級空間配置器最終封裝而成的 alloc) 作為空間配置器,並據此定義了一個 _Base。
class vector : protected _Vector_base<_Tp, _Alloc> { ... private: typedef _Vector_base<_Tp, _Alloc> _Base; ...
//以前是:
typedef simple_alloc<value_type, Alloc> data_allocator;
//最新版本是進行了進一步封裝,分開了基本參數和泛型操作函數
}
vector 提供了許多constructors,其中一個允許我們指定空間大小與初值:
//就是這個,允許我們指定空間大小和初值
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); } //上面 uninitialized_fill_n 這個函數,藏得很深在stl_uninitialized.h裏面,實際該函數還是對fill函數的一次封裝
//最終調用的是fill函數,在 stl_alogbase.h 這個頭文件下面,如下,
//不過這個文件夾下面有很多fill函數,我想應該是uninitialized_fill_n的作用還包括型別判斷,根據型別不同調用不同的fill函數
void fill(_ForwardIter __first, _ForwardIter __last, const _Tp& __value) { __STL_REQUIRES(_ForwardIter, _Mutable_ForwardIterator); for ( ; __first != __last; ++__first) *__first = __value; }
我們以 push_back() 函數將新元素插入vector尾端時,該函數首先檢查是否還有備用空間,如果有則直接在備用空間上構造元素,並調整叠代器finish,使得vector變大。如果沒有備用空間就擴充:
void push_back(const _Tp& __x) { if (_M_finish != _M_end_of_storage) { construct(_M_finish, __x); //直接placement構造元素,移動_M_finish指針 ++_M_finish; } else _M_insert_aux(end(), __x); //重新構造整個vector並插入元素 }
//表示不懂為啥要重載一次push_back()。。。 void push_back() { if (_M_finish != _M_end_of_storage) { construct(_M_finish); ++_M_finish; } else _M_insert_aux(end()); }
至於 _M_insert_aux 函數,有點復雜,但是也不難理解,如下:
vector<_Tp, _Alloc>::_M_insert_aux(iterator __position, const _Tp& __x) { if (_M_finish != _M_end_of_storage) //有備用空間
{
//在備用空間起始處構造一個元素,並以vector最後一個元素為其初始值 construct(_M_finish, *(_M_finish - 1)); ++_M_finish; //調整水位 _Tp __x_copy = __x; copy_backward(__position, _M_finish - 2, _M_finish - 1); *__position = __x_copy; } else //無備用空間
{ const size_type __old_size = size(); const size_type __len = __old_size != 0 ? 2 * __old_size : 1; //以上配置原則,如果原大小為0,則配置一個元素大小
//如果原大小不為零,則配置原來大小的兩倍
//前半段用來存放原始數據,後半段用來存放新數據
iterator __new_start = _M_allocate(__len); iterator __new_finish = __new_start; __STL_TRY
{
//原vector內容拷貝到新vector __new_finish = uninitialized_copy(_M_start, __position, __new_start); construct(__new_finish, __x); //為新元素設定初始x ++__new_finish; //調整水位
//安插點的原內容也一起拷貝 __new_finish = uninitialized_copy(__position, _M_finish, __new_finish); } __STL_UNWIND((destroy(__new_start,__new_finish), _M_deallocate(__new_start,__len)));
//銷毀,釋放原vector destroy(begin(), end()); _M_deallocate(_M_start, _M_end_of_storage - _M_start);
//調整叠代器,指向新的vector _M_start = __new_start; _M_finish = __new_finish; _M_end_of_storage = __new_start + __len; } }
因此可以明白,對 vector 的任何操作,一旦引起空間重新配置,則指向原 vector 的所有叠代器都會失效。
STL淺析——序列式容器vector的構造和內存管理: constructor() 和 push_back()