SGISTL原始碼閱讀十五 deque容器中
阿新 • • 發佈:2018-11-14
SGISTL原始碼閱讀十五 deque容器中
前言
我們已經學習了deque
的資料結構和它的迭代器,接下來我們繼續學習它的構造及記憶體申請等內容。
深入原始碼
deque
的建構函式
- 預設建構函式
/* 指標陣列不申請空間
* 並且first和finish迭代器也呼叫預設建構函式
*/
public:
deque()
: start(), finish(), map(0), map_size(0)
{
create_map_and_nodes(0);
}
- 拷貝建構函式
/* 分配了空間之後 * 將x中的元素拷貝過去 */ deque(const deque& x) : start(), finish(), map(0), map_size(0) { //申請指標陣列map的大小及線性空間(緩衝區buffer) create_map_and_nodes(x.size()); __STL_TRY { uninitialized_copy(x.begin(), x.end(), start); } __STL_UNWIND(destroy_map_and_nodes()); }
- 初始化n個值為value的元素
根據不同的情況設計了不同的過載版本,它們都呼叫了fill_initialize
函式
deque(size_type n, const value_type& value) : start(), finish(), map(0), map_size(0) { fill_initialize(n, value); } deque(int n, const value_type& value) : start(), finish(), map(0), map_size(0) { fill_initialize(n, value); } deque(long n, const value_type& value) : start(), finish(), map(0), map_size(0) { fill_initialize(n, value); }
- 構造n個值為預設值的元素
這裡的explicit
關鍵字已經見到過幾次了,用途是防止隱式轉換
explicit deque(size_type n)
: start(), finish(), map(0), map_size(0)
{
fill_initialize(n, value_type());
}
- 迭代器範圍構造
同樣根據不同的情況有多個不同的過載版本
#ifdef __STL_MEMBER_TEMPLATES template <class InputIterator> deque(InputIterator first, InputIterator last) : start(), finish(), map(0), map_size(0) { //range_initialize根據迭代器的型別選擇最優的初始化方式 range_initialize(first, last, iterator_category(first)); } #else /* __STL_MEMBER_TEMPLATES */ deque(const value_type* first, const value_type* last) : start(), finish(), map(0), map_size(0) { create_map_and_nodes(last - first); __STL_TRY { uninitialized_copy(first, last, start); } __STL_UNWIND(destroy_map_and_nodes()); } deque(const_iterator first, const_iterator last) : start(), finish(), map(0), map_size(0) { create_map_and_nodes(last - first); __STL_TRY { uninitialized_copy(first, last, start); } __STL_UNWIND(destroy_map_and_nodes()); } #endif /* __STL_MEMBER_TEMPLATES */
create_map_and_nodes
申請指標陣列map的大小及線性空間(緩衝區buffer)
template <class T, class Alloc, size_t BufSize>
void deque<T, Alloc, BufSize>::create_map_and_nodes(size_type num_elements) {
/* 計算結點個數
* buffer_size()返回每段線性空間能容量元素的個數
* num_elements代表需要申請的元素的個數
* num_nodes代表需要申請的結點個數
*/
size_type num_nodes = num_elements / buffer_size() + 1;
/* map至少有8個結點,最多num_nodes + 2
* 之所以要設計成num_nodes + 2
* 是為了當插入元素分別到尾部和首部時有一定的擴充套件性,而不用一插入元素就分配空間
*/
map_size = max(initial_map_size(), num_nodes + 2);
//申請空間
map = map_allocator::allocate(map_size);
map_pointer nstart = map + (map_size - num_nodes) / 2;
map_pointer nfinish = nstart + num_nodes - 1;
map_pointer cur;
__STL_TRY {
/* 申請一整段線性空間
* allocate_node的原型如下
* return data_allocator::allocate(buffer_size();
*/
for (cur = nstart; cur <= nfinish; ++cur)
*cur = allocate_node();
}
# ifdef __STL_USE_EXCEPTIONS
catch(...) {
//處理異常情況
for (map_pointer n = nstart; n < cur; ++n)
deallocate_node(*n);
map_allocator::deallocate(map, map_size);
throw;
}
# endif /* __STL_USE_EXCEPTIONS */
//維護start和finish迭代器
start.set_node(nstart);
finish.set_node(nfinish);
start.cur = start.first;
finish.cur = finish.first + num_elements % buffer_size();
}
deque
的初始化操作
fill_initialize
template <class T, class Alloc, size_t BufSize>
void deque<T, Alloc, BufSize>::fill_initialize(size_type n,
const value_type& value) {
//建立map和nodes
create_map_and_nodes(n);
map_pointer cur;
__STL_TRY {
//初始化每一個node節點
//初始化未初始化的值為value
for (cur = start.node; cur < finish.node; ++cur)
uninitialized_fill(*cur, *cur + buffer_size(), value);
//隨最後一個節點特殊處理,因為最後一個節點不一定會被填滿
uninitialized_fill(finish.first, finish.cur, value);
}
# ifdef __STL_USE_EXCEPTIONS
catch(...) {
//處理異常情況,將所有節點銷燬
for (map_pointer n = start.node; n < cur; ++n)
destroy(*n, *n + buffer_size());
destroy_map_and_nodes();
throw;
}
# endif /* __STL_USE_EXCEPTIONS */
}
range_initialize
在進行構造的時候傳入的迭代器也有可能是別的型別的迭代器,不一定是random_access_iterator
以下range_initialize
根據不同的情況寫了不同的過載版本
template <class T, class Alloc, size_t BufSize>
template <class InputIterator>
void deque<T, Alloc, BufSize>::range_initialize(InputIterator first,
InputIterator last,
input_iterator_tag) {
create_map_and_nodes(0);
for ( ; first != last; ++first)
//一個節點一個節點地插入
push_back(*first);
}
template <class T, class Alloc, size_t BufSize>
template <class ForwardIterator>
void deque<T, Alloc, BufSize>::range_initialize(ForwardIterator first,
ForwardIterator last,
forward_iterator_tag) {
size_type n = 0;
//計算出[first, last)範圍內的節點個數
distance(first, last, n);
//申請空間
create_map_and_nodes(n);
__STL_TRY {
//拷貝
uninitialized_copy(first, last, start);
}
//失敗則直接銷燬
__STL_UNWIND(destroy_map_and_nodes());
}
deque
的析構和記憶體釋放
解構函式
~deque() {
destroy(start, finish);
destroy_map_and_nodes();
}
destroy_map_and_nodes()
template <class T, class Alloc, size_t BufSize>
void deque<T, Alloc, BufSize>::destroy_map_and_nodes() {
for (map_pointer cur = start.node; cur <= finish.node; ++cur)
deallocate_node(*cur);
//釋放空間
map_allocator::deallocate(map, map_size);
}
總結
我們學習了deque
的構造,析構等。
我們可以看出deque
為了維護它獨特的資料結構付出了很多代價,很多的操作都變得異常艱難和複雜,但是隻要理解到它的資料結構這些都不難理解。接下來我們將繼續學習deque
的一些相關操作,要看懂這些程式碼的前提是我們必須深刻地理解deque
的資料結構。