STL原始碼剖析--vector
阿新 • • 發佈:2019-01-30
vector容器概述
vector的資料安排以及操作方式,與array非常相似。兩者的唯一區別在於空間的運用的靈活性。array是靜態空間,一旦配置了就不能改變;要換個大(或小)一點的房子,可以,一切瑣細都得由客戶端自己來:首先配置一塊新空間,然後將元素從舊址一一搬往新址,再把原來的空間釋還給系統。vector是動態空間,隨著元素的加入,它的內部機制會自行擴充空間以容納新元素。因此,vector的運用對於記憶體的合理利用與運用的靈活性有很大的幫助,我們再也不必因為害怕空間不足而一開始要求一個大塊頭的array了,我們可以安心使用array,吃多少用多少。
vector的實現技術,關鍵在於其對大小的控制以及重新配置時的資料移動效率。一旦vector的舊有空間滿載,如果客戶端每新增一個元素,vector的內部只是擴充一個元素的空間,實為不智。因為所謂擴充空間(不論多大),一如稍早所說,是”配置新空間/資料移動/釋還舊空間 “的大工程,時間成本很高,應該加入某種未雨綢繆的考慮。稍後我們便可看到SGI vector的空間配置策略了。
另外,由於vector維護的是一個連續線性空間,所以vector支援隨機存取。
注意:vector動態增加大小時,並不是在原空間之後持續新空間(因為無法保證原空間之後尚有可供配置的空間),而是以原大小的兩倍另外配置一塊較大的空間,然後將原內容拷貝過來,然後才開始在原內容之後構造新元素,並釋放原空間。因此,對vector的任何操作,一旦引起空間重新配置,指向原vector的所有迭代器就都失效了。這是程式設計師易犯的一個錯誤,務需小心。
vector的資料安排以及操作方式,與array非常相似。兩者的唯一區別在於空間的運用的靈活性。array是靜態空間,一旦配置了就不能改變;要換個大(或小)一點的房子,可以,一切瑣細都得由客戶端自己來:首先配置一塊新空間,然後將元素從舊址一一搬往新址,再把原來的空間釋還給系統。vector是動態空間,隨著元素的加入,它的內部機制會自行擴充空間以容納新元素。因此,vector的運用對於記憶體的合理利用與運用的靈活性有很大的幫助,我們再也不必因為害怕空間不足而一開始要求一個大塊頭的array了,我們可以安心使用array,吃多少用多少。
vector的實現技術,關鍵在於其對大小的控制以及重新配置時的資料移動效率。一旦vector的舊有空間滿載,如果客戶端每新增一個元素,vector的內部只是擴充一個元素的空間,實為不智。因為所謂擴充空間(不論多大),一如稍早所說,是”配置新空間/資料移動/釋還舊空間
另外,由於vector維護的是一個連續線性空間,所以vector支援隨機存取。
注意:vector動態增加大小時,並不是在原空間之後持續新空間(因為無法保證原空間之後尚有可供配置的空間),而是以原大小的兩倍另外配置一塊較大的空間,然後將原內容拷貝過來,然後才開始在原內容之後構造新元素,並釋放原空間。因此,對vector的任何操作,一旦引起空間重新配置,指向原vector的所有迭代器就都失效了。這是程式設計師易犯的一個錯誤,務需小心。
以下是vector定義的原始碼摘錄:
#include<iostream> using namespace std; #include<memory.h> // alloc是SGI STL的空間配置器 template <class T, class Alloc = alloc> class vector { public: // vector的巢狀型別定義,typedefs用於提供iterator_traits<I>支援 typedef T value_type; typedef value_type* pointer; typedef value_type* iterator; typedef value_type& reference; typedef size_t size_type; typedef ptrdiff_t difference_type; protected: // 這個提供STL標準的allocator介面 typedef simple_alloc <value_type, Alloc> data_allocator; iterator start; // 表示目前使用空間的頭 iterator finish; // 表示目前使用空間的尾 iterator end_of_storage; // 表示實際分配記憶體空間的尾 void insert_aux(iterator position, const T& x); // 釋放分配的記憶體空間 void deallocate() { // 由於使用的是data_allocator進行記憶體空間的分配, // 所以需要同樣使用data_allocator::deallocate()進行釋放 // 如果直接釋放, 對於data_allocator內部使用記憶體池的版本 // 就會發生錯誤 if (start) data_allocator::deallocate(start, end_of_storage - start); } void fill_initialize(size_type n, const T& value) { start = allocate_and_fill(n, value); finish = start + n; // 設定當前使用記憶體空間的結束點 // 構造階段, 此實作不多分配記憶體, // 所以要設定記憶體空間結束點和, 已經使用的記憶體空間結束點相同 end_of_storage = finish; } public: // 獲取幾種迭代器 iterator begin() { return start; } iterator end() { return finish; } // 返回當前物件個數 size_type size() const { return size_type(end() - begin()); } size_type max_size() const { return size_type(-1) / sizeof(T); } // 返回重新分配記憶體前最多能儲存的物件個數 size_type capacity() const { return size_type(end_of_storage - begin()); } bool empty() const { return begin() == end(); } reference operator[](size_type n) { return *(begin() + n); } // 本實作中預設構造出的vector不分配記憶體空間 vector() : start(0), finish(0), end_of_storage(0) {} vector(size_type n, const T& value) { fill_initialize(n, value); } vector(int n, const T& value) { fill_initialize(n, value); } vector(long n, const T& value) { fill_initialize(n, value); } // 需要物件提供預設建構函式 explicit vector(size_type n) { fill_initialize(n, T()); } vector(const vector<T, Alloc>& x) { start = allocate_and_copy(x.end() - x.begin(), x.begin(), x.end()); finish = start + (x.end() - x.begin()); end_of_storage = finish; } ~vector() { // 析構物件 destroy(start, finish); // 釋放記憶體 deallocate(); } vector<T, Alloc>& operator=(const vector<T, Alloc>& x); // 提供訪問函式 reference front() { return *begin(); } reference back() { return *(end() - 1); } //////////////////////////////////////////////////////////////////////////////// // 向容器尾追加一個元素, 可能導致記憶體重新分配 //////////////////////////////////////////////////////////////////////////////// // push_back(const T& x) // | // |---------------- 容量已滿? // | // ---------------------------- // No | | Yes // | | // ↓ ↓ // construct(finish, x); insert_aux(end(), x); // ++finish; | // |------ 記憶體不足, 重新分配 // | 大小為原來的2倍 // new_finish = data_allocator::allocate(len); <stl_alloc.h> // uninitialized_copy(start, position, new_start); <stl_uninitialized.h> // construct(new_finish, x); <stl_construct.h> // ++new_finish; // uninitialized_copy(position, finish, new_finish); <stl_uninitialized.h> //////////////////////////////////////////////////////////////////////////////// void push_back(const T& x) { // 記憶體滿足條件則直接追加元素, 否則需要重新分配記憶體空間 if (finish != end_of_storage) { construct(finish, x); ++finish; } else insert_aux(end(), x); } //////////////////////////////////////////////////////////////////////////////// // 在指定位置插入元素 //////////////////////////////////////////////////////////////////////////////// // insert(iterator position, const T& x) // | // |------------ 容量是否足夠 && 是否是end()? // | // ------------------------------------------- // No | | Yes // | | // ↓ ↓ // insert_aux(position, x); construct(finish, x); // | ++finish; // |-------- 容量是否夠用? // | // -------------------------------------------------- // Yes | | No // | | // ↓ | // construct(finish, *(finish - 1)); | // ++finish; | // T x_copy = x; | // copy_backward(position, finish - 2, finish - 1); | // *position = x_copy; | // ↓ // data_allocator::allocate(len); <stl_alloc.h> // uninitialized_copy(start, position, new_start); <stl_uninitialized.h> // construct(new_finish, x); <stl_construct.h> // ++new_finish; // uninitialized_copy(position, finish, new_finish); <stl_uninitialized.h> // destroy(begin(), end()); <stl_construct.h> // deallocate(); //////////////////////////////////////////////////////////////////////////////// iterator insert(iterator position, const T& x) { size_type n = position - begin(); if (finish != end_of_storage && position == end()) { construct(finish, x); ++finish; } else insert_aux(position, x); return begin() + n; } iterator insert(iterator position) { return insert(position, T()); } void pop_back() { --finish; destroy(finish); } iterator erase(iterator position) { if (position + 1 != end()) copy(position + 1, finish, position); --finish; destroy(finish); return position; } iterator erase(iterator first, iterator last) { iterator i = copy(last, finish, first); // 析構掉需要析構的元素 destroy(i, finish); finish = finish - (last - first); return first; } // 調整size, 但是並不會重新分配記憶體空間 void resize(size_type new_size, const T& x) { if (new_size < size()) erase(begin() + new_size, end()); else insert(end(), new_size - size(), x); } void resize(size_type new_size) { resize(new_size, T()); } void clear() { erase(begin(), end()); } protected: // 分配空間, 並且複製物件到分配的空間處 iterator allocate_and_fill(size_type n, const T& x) { iterator result = data_allocator::allocate(n); uninitialized_fill_n(result, n, x); return result; } // 提供插入操作 //////////////////////////////////////////////////////////////////////////////// // insert_aux(iterator position, const T& x) // | // |---------------- 容量是否足夠? // ↓ // ----------------------------------------- // Yes | | No // | | // ↓ | // 從opsition開始, 整體向後移動一個位置 | // construct(finish, *(finish - 1)); | // ++finish; | // T x_copy = x; | // copy_backward(position, finish - 2, finish - 1); | // *position = x_copy; | // ↓ // data_allocator::allocate(len); // uninitialized_copy(start, position, new_start); // construct(new_finish, x); // ++new_finish; // uninitialized_copy(position, finish, new_finish); // destroy(begin(), end()); // deallocate(); //////////////////////////////////////////////////////////////////////////////// template <class T, class Alloc> void insert_aux(iterator position, const T& x) { if (finish != end_of_storage) // 還有備用空間 { // 在備用空間起始處構造一個元素,並以vector最後一個元素值為其初值 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; // 以上配置元素:如果大小為0,則配置1(個元素大小) // 如果大小不為0,則配置原來大小的兩倍 // 前半段用來放置原資料,後半段準備用來放置新資料 iterator new_start = data_allocator::allocate(len); // 實際配置 iterator new_finish = new_start; // 將記憶體重新配置 try { // 將原vector的安插點以前的內容拷貝到新vector new_finish = uninitialized_copy(start, position, new_start); // 為新元素設定初值 x 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; } // 析構並釋放原vector destroy(begin(), end()); deallocate(); // 調整迭代器,指向新vector start = new_start; finish = new_finish; end_of_storage = new_start + len; } } //////////////////////////////////////////////////////////////////////////////// // 在指定位置插入n個元素 //////////////////////////////////////////////////////////////////////////////// // insert(iterator position, size_type n, const T& x) // | // |---------------- 插入元素個數是否為0? // ↓ // ----------------------------------------- // No | | Yes // | | // | ↓ // | return; // |----------- 記憶體是否足夠? // | // ------------------------------------------------- // Yes | | No // | | // |------ (finish - position) > n? | // | 分別調整指標 | // ↓ | // ---------------------------- | // No | | Yes | // | | | // ↓ ↓ | // 插入操作, 調整指標 插入操作, 調整指標 | // ↓ // data_allocator::allocate(len); // new_finish = uninitialized_copy(start, position, new_start); // new_finish = uninitialized_fill_n(new_finish, n, x); // new_finish = uninitialized_copy(position, finish, new_finish); // destroy(start, finish); // deallocate(); //////////////////////////////////////////////////////////////////////////////// template <class T, class Alloc> void insert(iterator position, size_type n, const T& x) { // 如果n為0則不進行任何操作 if (n != 0) { if (size_type(end_of_storage - finish) >= n) { // 剩下的備用空間大於等於“新增元素的個數” T x_copy = x; // 以下計算插入點之後的現有元素個數 const size_type elems_after = finish - position; iterator old_finish = finish; if (elems_after > n) { // 插入點之後的現有元素個數 大於 新增元素個數 uninitialized_copy(finish - n, finish, finish); finish += n; // 將vector 尾端標記後移 copy_backward(position, old_finish - n, old_finish); fill(position, position + n, x_copy); // 從插入點開始填入新值 } else { // 插入點之後的現有元素個數 小於等於 新增元素個數 uninitialized_fill_n(finish, n - elems_after, x_copy); finish += n - elems_after; uninitialized_copy(position, old_finish, finish); finish += elems_after; fill(position, old_finish, x_copy); } } else { // 剩下的備用空間小於“新增元素個數”(那就必須配置額外的記憶體) // 首先決定新長度:就長度的兩倍 , 或舊長度+新增元素個數 const size_type old_size = size(); const size_type len = old_size + max(old_size, n); // 以下配置新的vector空間 iterator new_start = data_allocator::allocate(len); iterator new_finish = new_start; __STL_TRY { // 以下首先將舊的vector的插入點之前的元素複製到新空間 new_finish = uninitialized_copy(start, position, new_start); // 以下再將新增元素(初值皆為n)填入新空間 new_finish = uninitialized_fill_n(new_finish, n, x); // 以下再將舊vector的插入點之後的元素複製到新空間 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(start, finish); deallocate(); start = new_start; finish = new_finish; end_of_storage = new_start + len; } } } };