前面介紹了STL對象的構造與析構以及內存的配置與釋放,那具體的容器是怎麼應用STL的空間配置器的呢?這篇先介紹STL的容器vector。html
vector只有4個數據成員:3個迭代器、1個內存配置器。git
STL會爲每一個容器都設置一個內存配置器的成員,這裏的內存配置器就是前面介紹的STL空間配置器,使用了統一對外接口的類simple_alloc,即STL會爲每一個容器都定義一個simple_alloc類的類型成員,經過該類型成員來爲容器分配內存。github
vector的迭代器就是原始指針,只不過用了typedef將迭代器的類型變爲了iterator,其實它就是T* 。vector的3個迭代器分別指向當前內存的起始地址(start)、最後一個數據的尾後地址(finish)、整個內存的最後地址(end_of_storage)。源碼以下:ide
class vector { public: typedef T value_type; typedef value_type* pointer; typedef const value_type* const_pointer; typedef value_type* iterator;//vector迭代器就是一個原生指針 typedef const value_type* const_iterator; typedef value_type& reference; typedef const value_type& const_reference; typedef size_t size_type; typedef ptrdiff_t difference_type; typedef MiniSTL::reverse_iterator<iterator> reverse_iterator; typedef MiniSTL::reverse_iterator<const_iterator> const_reverse_iterator; typedef alloc allocator_type; allocator_type get_allocator() const { return allocator_type(); } private: typedef simple_alloc<T, allocator_type> data_allocator; iterator start; iterator finish; iterator end_of_storage;
vector有多種構造函數,但作的事情都同樣,即先調用內存配置器去分配一塊內存,而後對這塊內存初始化,最後設置3個迭代器成員,讓它們指向正確的位置。函數
以咱們日常使用最多的vector構造方法,如:vector<int> vec(10); 爲例,其對應的構造函數以下,下面還將該構造過程涉及到的函數一併列出:源碼分析
構造函數vector(size_type n)調用fill_initializer函數,該函數會調用allocate_and_fill函數先去分配一塊內存,而後進行初始化,因爲這種構造方式未提供初始值,則按T類型的默認初始化進行初始化,而後剩下工做就是設置好start、finish、end_of_storage迭代器,工做便完成。spa
void fill_initializer(size_type n, const T& value) { start = allocate_and_fill(n, value); finish = start + n; end_of_storage = finish; } iterator allocate_and_fill(size_type n, const T& value) { iterator result = data_allocator::allocate(n); uninitialized_fill_n(result, n, value); return result; } public: vector() : start(0), finish(0), end_of_storage(0) {} explicit vector(size_type n) { fill_initializer(n, T()); }
當咱們向vector進行push_back時,若原內存空間未滿,那很好,直接在後面添加一個元素便可。若原內存空間已滿,則不能直接在其後面添加了,由於誰也不知道原空間後面的內存究竟是魔鬼仍是天使。指針
因此,若原空間內存已滿,繼續往vector添加元素時,會先調用內存配置數據成員,分配一塊新的內存,爲了減小內存分配的次數,因此既然要分配了,那乾脆就多分點,因此這塊新內存的大小爲原空間內存的2倍。code
接着,將原內存上的數據拷貝到新內存中--->析構原內存空間中的對象--->釋放原內存空間--->從新設置迭代器。htm
vector添加元素時,會致使內存空間的從新分配,因此會致使以前的迭代器都失效。
vector要是常常這樣動態增加會致使程序效率降低,因此能夠調用vector的reserve函數預先分配一大塊指定大小的內存,以減小內存重分配次數。
對照下面源碼分析。當finish和end_of_storage相等,則知道已經沒有剩餘空間了,push_back會調用insert_aux函數完成剩下所有工做。insert_aux函數調用allocate函數分配原空間2倍大小的新內存空間,調用uninitialized_copy函數將原內存中數據拷貝到新內存,接着調用destroy析構原空間中對象,調用deallocate()釋放原內存空間,並從新設置start、finish、end_of_storage迭代器。以下:
push_back :
void push_back(const T& val) { if (finish != end_of_storage) { construct(finish, val); ++finish; } else insert_aux(end(), val); }
insert_aux :
template<typename T> void vector<T>::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; //2倍原空間大小 iterator new_start = data_allocator::allocate(len); 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, len); throw; } destroy(begin(), end()); // 析構原內存空間的對象 deallocate(start, end_of_storage - start); // 釋放原內存空間 start = new_start; finish = new_finish; end_of_storage = new_start + len; } }
有關vector空間的動態增加的詳細介紹可參考個人另外一篇文章:http://www.cnblogs.com/zxiner/p/7197327.html