1. 程式人生 > >SGISTL原始碼閱讀十六 deque容器下

SGISTL原始碼閱讀十六 deque容器下

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_frontpush_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的學習也就告一段落了。