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的学习也就告一段落了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值