SGISTL原始碼閱讀十六 deque容器下
阿新 • • 發佈:2018-11-14
SGISTL原始碼閱讀十六 deque容器下
前言
通過之前的學習我們對deque
已經有了一個比較深入的瞭解,如圖所示:
接下來將繼續學習deque
的相關操作
深入原始碼
插入操作
向deque
末尾插入一個元素
void push_back(const value_type& t) { //如果deque還有足夠的容量 if (finish.cur != finish.last - 1) { //以值為t構造該位置 construct(finish.cur, t); //維護deque的迭代器 ++finish.cur; } else //交給push_back_aux處理 push_back_aux(t); } //通過push_back函式我們知道,只需要再多一塊空間,就能把新元素插入進去 //那麼就新建一個節點,把新元素放在原finish.last的位置上去 template <class T, class Alloc, size_t BufSize> void deque<T, Alloc, BufSize>::push_back_aux(const value_type& t) { value_type t_copy = t; reserve_map_at_back(); //申請新的節點空間(等價於申請一段新的線性空間) *(finish.node + 1) = allocate_node(); __STL_TRY { //以值為t構造該位置 construct(finish.cur, t_copy); //維護deque的迭代器 finish.set_node(finish.node + 1); finish.cur = finish.first; } //處理異常,釋放新申請的node __STL_UNWIND(deallocate_node(*(finish.node + 1))); }
向deque
頭部插入一個元素
其實push_front
和push_back
極其相似,只是一個向前擴容,一個向後擴容,可以對照著分析,就不貼註釋了。
void push_front(const value_type& t) { if (start.cur != start.first) { construct(start.cur - 1, t); --start.cur; } else push_front_aux(t); } template <class T, class Alloc, size_t BufSize> void deque<T, Alloc, BufSize>::push_front_aux(const value_type& t) { value_type t_copy = t; reserve_map_at_front(); *(start.node - 1) = allocate_node(); __STL_TRY { start.set_node(start.node - 1); start.cur = start.last - 1; construct(start.cur, t_copy); } # ifdef __STL_USE_EXCEPTIONS catch(...) { start.set_node(start.node + 1); start.cur = start.first; deallocate_node(*(start.node - 1)); throw; } # endif /* __STL_USE_EXCEPTIONS */ }
insert
insert_aux
insert_aux
函式是指定位置插入的關鍵函式,根據不同的插入方式,對應著不同的過載版本,因為版本過多就不一一分析,他們的核心思想都是一個意思,這裡只分析指定位置插入一個元素的情況
//指定位置插入一個元素 template <class T, class Alloc, size_t BufSize> typename deque<T, Alloc, BufSize>::iterator deque<T, Alloc, BufSize>::insert_aux(iterator pos, const value_type& x) { //計算出插入點之前的元素個數 difference_type index = pos - start; value_type x_copy = x; /* 要在非頭/尾位置插入元素,對於線性空間而言,勢必涉及到移動元素 * 為了使得效率達到最優,需要儘量少移動元素 * 所以接下來我們比較插入點前的元素個數和插入點之後的元素個數 * 然後來選擇移動哪一邊的元素 */ if (index < size() / 2) { //插入點前的元素較少 //在首部插入一個與第一個元素相等的值 push_front(front()); iterator front1 = start; ++front1; iterator front2 = front1; ++front2; pos = start + index; iterator pos1 = pos; ++pos1; //移動元素 copy(front2, pos1, front1); } else { //插入點後的元素較少 //在尾部插入一個與最後一個元素相等的值 push_back(back()); iterator back1 = finish; --back1; iterator back2 = back1; --back2; pos = start + index; //移動元素 copy_backward(pos, back2, back1); } //賦值操作 *pos = x_copy; return pos; } //...
- 指定位置插入一個元素
iterator insert(iterator position, const value_type& x) {
//符合向前插入
if (position.cur == start.cur) {
push_front(x);
return start;
}
//符合向後插入
else if (position.cur == finish.cur) {
push_back(x);
iterator tmp = finish;
--tmp;
return tmp;
}
else {
return insert_aux(position, x);
}
}
//指定位置插入一個預設值
iterator insert(iterator position) { return insert(position, value_type()); }
- 指定位置插入n個元素
//函式宣告
void insert(iterator pos, size_type n, const value_type& x);
void insert(iterator pos, int n, const value_type& x) {
insert(pos, (size_type) n, x);
}
void insert(iterator pos, long n, const value_type& x) {
insert(pos, (size_type) n, x);
}
//...
template <class T, class Alloc, size_t BufSize>
void deque<T, Alloc, BufSize>::insert(iterator pos,
size_type n, const value_type& x) {
//如果滿足頭部插入
if (pos.cur == start.cur) {
//這個函式的作用是在頭部new n個elements,並返回新start
iterator new_start = reserve_elements_at_front(n);
//初始化未初始化的空間
uninitialized_fill(new_start, start, x);
start = new_start;
}
//如果滿足尾部插入,與頭部插入同理
else if (pos.cur == finish.cur) {
iterator new_finish = reserve_elements_at_back(n);
uninitialized_fill(finish, new_finish, x);
finish = new_finish;
}
else
//呼叫相應版本的insert_aux函式
insert_aux(pos, n, x);
}
- 迭代器指定範圍插入
它的版本也不止一種,但是核心思想是一樣的,所以只拿出其中一個來講。
#ifdef __STL_MEMBER_TEMPLATES
template <class InputIterator>
void insert(iterator pos, InputIterator first, InputIterator last) {
insert(pos, first, last, iterator_category(first));
}
#else /* __STL_MEMBER_TEMPLATES */
void insert(iterator pos, const value_type* first, const value_type* last);
void insert(iterator pos, const_iterator first, const_iterator last);
template <class T, class Alloc, size_t BufSize>
void deque<T, Alloc, BufSize>::insert(iterator pos,
const value_type* first,
const value_type* last) {
size_type n = last - first;
//滿足從頭插入
if (pos.cur == start.cur) {
//這個函式的作用是在頭部new n個elements,並返回新start
iterator new_start = reserve_elements_at_front(n);
__STL_TRY {
//將迭代器first和last指定範圍的元素拷貝到deque中去
uninitialized_copy(first, last, new_start);
start = new_start;
}
//處理異常情況
__STL_UNWIND(destroy_nodes_at_front(new_start));
}
//滿足從尾部插入,與頭部插入類似
else if (pos.cur == finish.cur) {
iterator new_finish = reserve_elements_at_back(n);
__STL_TRY {
uninitialized_copy(first, last, finish);
finish = new_finish;
}
__STL_UNWIND(destroy_nodes_at_back(new_finish));
}
else
insert_aux(pos, first, last, n);
}
刪除操作
從deque
末尾刪除一個元素
void pop_back() {
/* 最後一段線性空間有一個及以上元素時則只用簡單的移動cur以及析構當前元素
* 否則呼叫pop_back_aux
*/
if (finish.cur != finish.first) {
--finish.cur;
destroy(finish.cur);
}
else
pop_back_aux();
}
//呼叫pop_back_aux()則說明當前節點只有一個元素了
//所以直接銷燬這個節點
template <class T, class Alloc, size_t BufSize>
void deque<T, Alloc, BufSize>:: pop_back_aux() {
//銷燬該節點
deallocate_node(finish.first);
finish.set_node(finish.node - 1);
//維護deque的迭代器
finish.cur = finish.last - 1;
destroy(finish.cur);
}
從deque
頭部刪除一個元素
/* 最後一段線性空間有一個及以上元素時則只用簡單的移動cur以及析構當前元素
* 否則呼叫pop_front_aux
*/
void pop_front() {
if (start.cur != start.last - 1) {
destroy(start.cur);
++start.cur;
}
else
pop_front_aux();
}
//呼叫pop_front_aux()則說明當前節點只有一個元素了
//所以直接銷燬這個節點
template <class T, class Alloc, size_t BufSize>
void deque<T, Alloc, BufSize>::pop_front_aux() {
destroy(start.cur);
deallocate_node(start.first);
start.set_node(start.node + 1);
start.cur = start.first;
}
erase
deque
通過erase
來指定位置刪除元素
- 刪除指定位置的一個元素
/* 由於是線性空間
* 所以刪除元素時也涉及到元素的移動
* 思想和insert_aux相同
* 選擇較少的元素移動
*/
iterator erase(iterator pos) {
iterator next = pos;
++next;
difference_type index = pos - start;
if (index < (size() >> 1)) {
copy_backward(start, pos, next);
pop_front();
}
else {
copy(next, finish, pos);
pop_back();
}
return start + index;
}
- 迭代器範圍刪除
deque<T, Alloc, BufSize>::erase(iterator first, iterator last) {
//刪除整個deque,直接呼叫clear函式
if (first == start && last == finish) {
clear();
return finish;
}
else {
/* 這裡同樣也要判斷移動哪一端的元素較少
* 來選擇效率較高的方法
*/
difference_type n = last - first;
difference_type elems_before = first - start;
if (elems_before < (size() - n) / 2) {
copy_backward(start, first, last);
iterator new_start = start + n;
//移動結束之後,對空間進行釋放
destroy(start, new_start);
for (map_pointer cur = start.node; cur < new_start.node; ++cur)
data_allocator::deallocate(*cur, buffer_size());
start = new_start;
}
else {
copy(last, finish, first);
iterator new_finish = finish - n;
destroy(new_finish, finish);
for (map_pointer cur = new_finish.node + 1; cur <= finish.node; ++cur)
data_allocator::deallocate(*cur, buffer_size());
finish = new_finish;
}
return start + elems_before;
}
}
clear()
template <class T, class Alloc, size_t BufSize>
void deque<T, Alloc, BufSize>::clear() {
//通過迴圈依次銷燬節點
for (map_pointer node = start.node + 1; node < finish.node; ++node) {
destroy(*node, *node + buffer_size());
data_allocator::deallocate(*node, buffer_size());
}
/* 若還存在兩個緩衝區及以上
* 則對其元素進行析構
* 最後將第一個線性空間保留下來
*/
if (start.node != finish.node) {
destroy(start.cur, start.last);
destroy(finish.first, finish.cur);
data_allocator::deallocate(finish.first, buffer_size());
}
else
//將唯一的線性空間上的元素進行析構
destroy(start.cur, finish.cur);
//更新迭代器
finish = start;
}
總結
我們學習了deque
的插入、刪除相關操作。
對deque
的學習也就告一段落了。