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