SGISTL原始碼閱讀八 Vector容器上
SGISTL原始碼閱讀八 Vector容器上
Vector概述
array
是靜態的陣列,一經定義大小不可改變,這對於未知陣列大小的情況來說是非常不利的,如果沒有vector
我們只能猜測一個較大的值來初始化陣列。這無疑對空間造成了很大程度上的浪費,而且如果初始值過小,就得必須自己重新定義一個更大的陣列,並且將原來的資料拷貝一份,再釋放掉原來的空間。
vector
被稱為動態陣列。隨著元素的加入,它的內部機制會自行擴充空間以容納新元素,它相對於array
來說,更加的靈活。
深入Vector原始碼
Vector的迭代器及其資料結構
//預設使用SGISTL空間配置器 template <class T, class Alloc = alloc> class vector { public: //vector的巢狀型別定義 typedef T value_type; typedef value_type* pointer; typedef const value_type* const_pointer; typedef value_type* iterator; //它的迭代器就是普通指標 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;
我們知道vector
所維護的是一個連續的線性空間,普通指標就可以作為vector的迭代器。
(在iterator_traits
的學習中,我們知道指標的迭代器型別是random_access_iterator
。)
protected: //... iterator start; //表示目前使用空間的頭 iterator finish; //表示目前使用空間的尾 iterator end_of_storage; //表示目前可用空間的尾 //... public: //由於vector內部的迭代器是保護型別的,所以需要一個對外的介面 iterator begin() { return start; } const_iterator begin() const { return start; } iterator end() { return finish; } const_iterator end() const { return finish; } //... //返回vector的已使用空間 size_type size() const { return size_type(end() - begin()); } size_type max_size() const { return size_type(-1) / sizeof(T); } //返回vector的容量 size_type capacity() const { return size_type(end_of_storage - begin()); } bool empty() const { return begin() == end(); }
這裡我們可以看到vector
的結構是由三個迭代器來維護的,分別指向了vector
使用空間的頭部,尾部,以及可用空間的尾部。
如圖:
vector的構造與記憶體分配
vector的建構函式
//預設建構函式,不申請空間 vector() : start(0), finish(0), end_of_storage(0) {} //接受兩個引數,第一個引數 指定vector初始化大小,第二個引數為初始化值(所有空間都會被初始化成value) //以下三個是針對不同情況的過載版本,他們都呼叫了fill_initialize函式 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); } //接受一個引數,指定vector的初始化大小。 //詳述 explicit vector(size_type n) { fill_initialize(n, T()); } //拷貝建構函式 vector(const vector<T, Alloc>& x) { //呼叫了allocate_and_copy函式 start = allocate_and_copy(x.end() - x.begin(), x.begin(), x.end()); finish = start + (x.end() - x.begin()); end_of_storage = finish; } //以下兩個建構函式是根據first和last兩個迭代器的範圍來進行初始化 #ifdef __STL_MEMBER_TEMPLATES template <class InputIterator> vector(InputIterator first, InputIterator last) : start(0), finish(0), end_of_storage(0) { //呼叫了range_initialize range_initialize(first, last, iterator_category(first)); } #else /* __STL_MEMBER_TEMPLATES */ vector(const_iterator first, const_iterator last) { size_type n = 0; distance(first, last, n); start = allocate_and_copy(n, first, last); finish = start + n; end_of_storage = finish; }
explicit vector(size_type n) { fill_initialize(n, T()); }
這個建構函式接受一個引數,指定了vector
的初始化大小。explicit
關鍵字是用來防止當只有一個傳入引數時發生隱式轉換的。
例如vector<int> v = 10;
,vector<int> v = 'a'
如果沒有explicit
關鍵字,以上兩種寫法是正確的。因為在C++中, 如果的建構函式只有一個引數時, 那麼在編譯的時候就會有一個預設的轉換操作:將該建構函式對應資料型別的資料轉換為該類物件. 也就是說 vector<int> v = 10;
這段程式碼, 編譯器自動將整型轉換為vector
類物件, 實際上等同於下面的操作:
vector<int> temp(10);
vector<int>v = temp;
vector的記憶體分配
在vector
的建構函式分析中,我們可以看到它們呼叫了fill_initialize
,allocate_and_copy
,range_initialize
函式,下面我們就來看一下這些函式的實現。
fill_initialize
fill_initializ
//維護了vector的三個迭代器,呼叫了allocate_and_fill函式
void fill_initialize(size_type n, const T& value) {
start = allocate_and_fill(n, value);
finish = start + n;
end_of_storage = finish;
}
allocate_and_fill
protected:
//typedef simple_alloc<value_type, Alloc> data_allocator;
//vector給SGISTL的空間配置器設定了一個別名
iterator allocate_and_fill(size_type n, const T& x) {
iterator result = data_allocator::allocate(n);
/* __STL_TRY...__STL_UNWIND類似異常處理的try...catch語句塊
* 這段程式碼的大意就是,初始化allocate分配的未初始化空間
* 如果失敗了,則將分配的記憶體回收,防止記憶體洩露
= */
__STL_TRY {
//呼叫初始化uninitialized_fill_n未初始化的空間
uninitialized_fill_n(result, n, x);
return result;
}
__STL_UNWIND(data_allocator::deallocate(result, n));
}
allocate_and_copy
//allocate_and_copy整體和allocate_and_fill類似,只是他們初始化未初始化空間呼叫的函式不同
#ifdef __STL_MEMBER_TEMPLATES
template <class ForwardIterator>
iterator allocate_and_copy(size_type n,
ForwardIterator first, ForwardIterator last) {
iterator result = data_allocator::allocate(n);
__STL_TRY {
uninitialized_copy(first, last, result);
return result;
}
__STL_UNWIND(data_allocator::deallocate(result, n));
}
#else /* __STL_MEMBER_TEMPLATES */
iterator allocate_and_copy(size_type n,
const_iterator first, const_iterator last) {
iterator result = data_allocator::allocate(n);
__STL_TRY {
uninitialized_copy(first, last, result);
return result;
}
__STL_UNWIND(data_allocator::deallocate(result, n));
}
range_initialize
//根據迭代器的不同版本,過載了不同版本
template <class InputIterator>
void range_initialize(InputIterator first, InputIterator last,
input_iterator_tag) {
for ( ; first != last; ++first)
push_back(*first);
}
// This function is only called by the constructor. We have to worry
// about resource leaks, but not about maintaining invariants.
template <class ForwardIterator>
void range_initialize(ForwardIterator first, ForwardIterator last,
forward_iterator_tag) {
size_type n = 0;
distance(first, last, n);
start = allocate_and_copy(n, first, last);
finish = start + n;
end_of_storage = finish;
}
總結
我們介紹了vector
的資料結構,構造及記憶體分配的相關內容。
之後我們將繼續介紹vector
的相關操作。