STL源码阅读-deque
来源:互联网 发布:js的slice方法 编辑:程序博客网 时间:2024/05/07 21:48
<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">vector提供单向开口的线性空间,而deque提供的则是双向开口的连续线性空间,当然这种线性空间并不是真正连续的。 deque允许于常数时间内对起痘团进行元素的插入或者移除操作,并且没有所谓的容量(capacity)的概念,以动态的分段连续内存空间组合而成,随时可以增加一段新的空间并连接起来。</span>
deque实现这种连续空间的组合,关键在于有一个中央控制器,从而对外呈现一个完整的整体。
去掉比较繁琐的typedef以及其他比较关键的函数的deque的定义如下,:
template <class _Tp, class _Alloc = __STL_DEFAULT_ALLOCATOR(_Tp) >class deque : protected _Deque_base<_Tp, _Alloc> { // requirements: __STL_CLASS_REQUIRES(_Tp, _Assignable); typedef _Deque_base<_Tp, _Alloc> _Base;public: // Basic types typedef _Tp value_type; typedef value_type* pointer; typedef const value_type* const_pointer; typedef value_type& reference; typedef const value_type& const_reference; typedef size_t size_type; typedef ptrdiff_t difference_type; typedef typename _Base::allocator_type allocator_type; allocator_type get_allocator() const { return _Base::get_allocator(); }public: // Iterators typedef typename _Base::iterator iterator; typedef typename _Base::const_iterator const_iterator;#ifdef __STL_CLASS_PARTIAL_SPECIALIZATION typedef reverse_iterator<const_iterator> const_reverse_iterator; typedef reverse_iterator<iterator> reverse_iterator;#else /* __STL_CLASS_PARTIAL_SPECIALIZATION */ typedef reverse_iterator<const_iterator, value_type, const_reference, difference_type> const_reverse_iterator; typedef reverse_iterator<iterator, value_type, reference, difference_type> reverse_iterator; #endif /* __STL_CLASS_PARTIAL_SPECIALIZATION */protected: // Internal typedefs typedef pointer* _Map_pointer; static size_t _S_buffer_size() { return __deque_buf_size(sizeof(_Tp)); }protected:public: // Basic accessors iterator begin() { return _M_start; } iterator end() { return _M_finish; } const_iterator begin() const { return _M_start; } const_iterator end() const { return _M_finish; } reverse_iterator rbegin() { return reverse_iterator(_M_finish); } reverse_iterator rend() { return reverse_iterator(_M_start); } const_reverse_iterator rbegin() const { return const_reverse_iterator(_M_finish); } const_reverse_iterator rend() const { return const_reverse_iterator(_M_start); } reference operator[](size_type __n) { return _M_start[difference_type(__n)]; } const_reference operator[](size_type __n) const { return _M_start[difference_type(__n)]; }首先关注的第一个函数是_S_buffer_size(),它是对函数__deque_buf_size的简单封装。
static size_t _S_buffer_size() { return __deque_buf_size(sizeof(_Tp)); }
其中__deque_buf_size的定义如下:
inline size_t __deque_buf_size(size_t __size) { return __size < 512 ? size_t(512 / __size) : size_t(1);}从上面可以看到,deque定义的缓冲区大小是512bytes,如果对象tp的大小大于512bytes,那么每个缓冲区的容量就只有一个tp,否则就是(512/sizeof(tp))个对象了。
deque的结构比较复杂,要理解deque设计细节,首先需要理解deque在内存组织上的特点,下面是deque组织结构示意图。
map是一个连续的内存空间,用于存储缓存空间的首地址,map的地址不够用的时候,就会重新申请map的空间,并且将原来存储的地址更新到新的位置。
在讨论deque实现细节之前,先从宏观上看一下如果希望实现上述的组织结构图,那么应该怎么做呢:
1.在尾部增加一个数据,如果数据空间够用,那么直接增加,finish->cur++就可以了;如果存储空间不够,需要申请新的内存,并且需要在map中注册,同时更新finish,指向新的缓存空间;如果此时map的存储空间不够用了,就应当申请新的map空间,将map的地址扩展过去,并且更新start和finish的node节点指针的值,按照这种思路,如果插入数据之后,那么原来的iterator会失效,因为iterator的node节点的值有可能会被修改,这样就没有办法跳转到下一个节点了。。。。好悲惨的说
2.如果在首部增加一个数据,如果存储空间够用,那么直接增加,start->cur--就可以了,如果存储空间不够用,就需要申请新的存储空间了,这个过程与在尾部增加一个数据差不多。
分析deque的内部实现细节,从deque的构造函数开始,deque的构造函数如下。
explicit deque(const allocator_type& __a = allocator_type()) : _Base(__a, 0) {} deque(const deque& __x) : _Base(__x.get_allocator(), __x.size()) { uninitialized_copy(__x.begin(), __x.end(), _M_start); } deque(size_type __n, const value_type& __value, const allocator_type& __a = allocator_type()) : _Base(__a, __n) { _M_fill_initialize(__value); } explicit deque(size_type __n) : _Base(allocator_type(), __n) { _M_fill_initialize(value_type()); }可以看到,这个初始化里面都调用了函数_Base进行自开始的空间初始化。_Base的定义如下:
typedef _Deque_base<_Tp, _Alloc> _Base;然后,看一下_Deque_base的定义,
template <class _Tp, class _Alloc>class _Deque_base {public: typedef _Deque_iterator<_Tp,_Tp&,_Tp*> iterator; typedef _Deque_iterator<_Tp,const _Tp&,const _Tp*> const_iterator; typedef _Alloc allocator_type; allocator_type get_allocator() const { return allocator_type(); } _Deque_base(const allocator_type&, size_t __num_elements) : _M_map(0), _M_map_size(0), _M_start(), _M_finish() { _M_initialize_map(__num_elements);//根据__num_elements进行初始化存储空间 } _Deque_base(const allocator_type&) : _M_map(0), _M_map_size(0), _M_start(), _M_finish() {} ~_Deque_base(); protected: void _M_initialize_map(size_t); void _M_create_nodes(_Tp** __nstart, _Tp** __nfinish); void _M_destroy_nodes(_Tp** __nstart, _Tp** __nfinish); enum { _S_initial_map_size = 8 };protected: _Tp** _M_map; size_t _M_map_size; iterator _M_start; iterator _M_finish; typedef simple_alloc<_Tp, _Alloc> _Node_alloc_type; typedef simple_alloc<_Tp*, _Alloc> _Map_alloc_type; _Tp* _M_allocate_node() { return _Node_alloc_type::allocate(__deque_buf_size(sizeof(_Tp))); } void _M_deallocate_node(_Tp* __p) { _Node_alloc_type::deallocate(__p, __deque_buf_size(sizeof(_Tp))); } _Tp** _M_allocate_map(size_t __n) { return _Map_alloc_type::allocate(__n); } void _M_deallocate_map(_Tp** __p, size_t __n) { _Map_alloc_type::deallocate(__p, __n); }};
从上面可以看到,deque在初始化的时候,会调用父类_Deque_base里面的构造函数_Deque_base(const allocator_type&, size_t __num_elements),这个函数最终调用的是 _M_initialize_map(__num_elements);。为了表述方便,M空间为存储map索引的空间,D空间为存储实际数据的缓存空间。该函数的原形如下:
template <class _Tp, class _Alloc>void_Deque_base<_Tp,_Alloc>::_M_initialize_map(size_t __num_elements){ size_t __num_nodes = __num_elements / __deque_buf_size(sizeof(_Tp)) + 1;//分配缓存空间,至少有一个单位的缓存空间 _M_map_size = max((size_t) _S_initial_map_size, __num_nodes + 2);//enum { _S_initial_map_size = 8 };,最小的map M空间是8个。 _M_map = _M_allocate_map(_M_map_size);//申请存放map的M空间 _Tp** __nstart = _M_map + (_M_map_size - __num_nodes) / 2; _Tp** __nfinish = __nstart + __num_nodes; __STL_TRY { _M_create_nodes(__nstart, __nfinish);//为start与finish之间的map分配对应的D空间。 } __STL_UNWIND((_M_deallocate_map(_M_map, _M_map_size), _M_map = 0, _M_map_size = 0)); _M_start._M_set_node(__nstart);//设置开始节点 _M_finish._M_set_node(__nfinish - 1); _M_start._M_cur = _M_start._M_first; _M_finish._M_cur = _M_finish._M_first + __num_elements % __deque_buf_size(sizeof(_Tp));//最后一个节点中的cur指向的空间位置,应当是去掉整数deque_buf_size剩余的。}可以看出来,deque在初始化的时候,D空间会比需求的多一个,至少会分配一个缓存D空间,以及至少8个存储map索引的M空间,如果需要的map节点大于8个,那么会分配的存储空间S+2个空间。上面的函数里面,调用函数 _M_allocate_map用于分配map索引的存储空间,该函数原型如下,直接调用alloc用来分配空间的。
typedef simple_alloc<_Tp*, _Alloc> _Map_alloc_type;_Tp** _M_allocate_map(size_t __n) { return _Map_alloc_type::allocate(__n); }此外,上面的函数里面,还调用了函数_M_create_nodes用于分配缓存D空间,该函数的原形如下:
template <class _Tp, class _Alloc>void _Deque_base<_Tp,_Alloc>::_M_create_nodes(_Tp** __nstart, _Tp** __nfinish)//传入的参数为[nstart,nfinish){ _Tp** __cur; __STL_TRY { for (__cur = __nstart; __cur < __nfinish; ++__cur) *__cur = _M_allocate_node();//直接调用createnode函数//分配用于缓存的D空间 } __STL_UNWIND(_M_destroy_nodes(__nstart, __cur));}
//调用alloc来分配存储空间typedef simple_alloc<_Tp, _Alloc> _Node_alloc_type;_Tp* _M_allocate_node() { return _Node_alloc_type::allocate(__deque_buf_size(sizeof(_Tp))); }到这里,基本上deque的空间分配算是完结了,如果需要插入新的数据,当空间不足时,其实还是一样的会进行类似的数据分配。只不过需要修改map索引以及更新一些其他的标签。
要实现对于deque的访问,最离不开的是iterator,它是保障deque对外呈现单一连续空间的工程,iterator的部分定义如下:
template <class _Tp, class _Ref, class _Ptr>struct _Deque_iterator { typedef _Deque_iterator<_Tp, _Tp&, _Tp*> iterator; typedef _Deque_iterator<_Tp, const _Tp&, const _Tp*> const_iterator; static size_t _S_buffer_size() { return __deque_buf_size(sizeof(_Tp)); } typedef random_access_iterator_tag iterator_category; typedef _Tp value_type; typedef _Ptr pointer; typedef _Ref reference; typedef size_t size_type; typedef ptrdiff_t difference_type; typedef _Tp** _Map_pointer; typedef _Deque_iterator _Self; _Tp* _M_cur;//当前D空间中使用的地址 _Tp* _M_first;//标记当前D空间的起始地址 _Tp* _M_last;//标记当前D空间的完结地址 _Map_pointer _M_node;//标记map中的地址位 _Deque_iterator(_Tp* __x, _Map_pointer __y) : _M_cur(__x), _M_first(*__y), _M_last(*__y + _S_buffer_size()), _M_node(__y) {} _Deque_iterator() : _M_cur(0), _M_first(0), _M_last(0), _M_node(0) {} _Deque_iterator(const iterator& __x) : _M_cur(__x._M_cur), _M_first(__x._M_first), _M_last(__x._M_last), _M_node(__x._M_node) {} reference operator*() const { return *_M_cur; }#ifndef __SGI_STL_NO_ARROW_OPERATOR pointer operator->() const { return _M_cur; }#endif /* __SGI_STL_NO_ARROW_OPERATOR */
从最上面的deque的结构组织图实现可以看出,iterator在实现的时候,利用cur、first、last以及node来实现map、以及缓存D空间之间的胶合的。
iterator最重要的就是++、--等操作了。逐一看一下这些函数的实现。
reference operator*() const { return *_M_cur; }//返回iterator指向的缓存D空间的值#ifndef __SGI_STL_NO_ARROW_OPERATOR pointer operator->() const { return _M_cur; }//返回iterator指向的缓存D空间的指针#endif /* __SGI_STL_NO_ARROW_OPERATOR */
difference_type operator-(const _Self& __x) const { return difference_type(_S_buffer_size()) * (_M_node - __x._M_node - 1) +//-1是因为后面的计算多了1个buffer的距离 (_M_cur - _M_first) + (__x._M_last - __x._M_cur); }
++操作
_Self& operator++() { ++_M_cur; if (_M_cur == _M_last) {//是否已经到了尾部 _M_set_node(_M_node + 1); _M_cur = _M_first; } return *this; } _Self operator++(int) { _Self __tmp = *this; ++*this; return __tmp; }_M_set_node函数定义如下;
void _M_set_node(_Map_pointer __new_node) { _M_node = __new_node; _M_first = *__new_node; _M_last = _M_first + difference_type(_S_buffer_size()); }++操作如果到达了结尾,那么直接调到下一个节点就可以了,然后重新设置一下各个标签。
--操作 的函数原型如下:
_Self& operator--() { if (_M_cur == _M_first) {//如果已经到了左边界 _M_set_node(_M_node - 1);// 跳转到左边的下一个节点 _M_cur = _M_last; } --_M_cur; return *this; } _Self operator--(int) { _Self __tmp = *this; --*this; return __tmp; }由于first和last标记的空间是[frist,last),所以++和--的操作顺序是有所不同的。
+=操作使得iterator拥有了randomaccess的功能,其原型如下:
_Self& operator+=(difference_type __n) { difference_type __offset = __n + (_M_cur - _M_first); if (__offset >= 0 && __offset < difference_type(_S_buffer_size()))//如果offset还在同一个节点范围内,直接加上就可以了 _M_cur += __n; else { difference_type __node_offset = __offset > 0 ? __offset / difference_type(_S_buffer_size())//如果cur>0,那么需要进行偏移 : -difference_type((-__offset - 1) / _S_buffer_size()) - 1;//如果<0 ,<span style="font-family: Arial, Helvetica, sans-serif;">从尾部开始数的话,</span><span style="font-family: Arial, Helvetica, sans-serif;">需要-1,</span> _M_set_node(_M_node + __node_offset); _M_cur = _M_first + (__offset - __node_offset * difference_type(_S_buffer_size()));//计算出相应的node值 } return *this; }
剩下的iterator的操作就比较容易理解了,基本上都是对上面几个功能的略加封装。
_Self operator+(difference_type __n) const { _Self __tmp = *this; return __tmp += __n; } _Self& operator-=(difference_type __n) { return *this += -__n; } _Self operator-(difference_type __n) const { _Self __tmp = *this; return __tmp -= __n; } reference operator[](difference_type __n) const { return *(*this + __n); } bool operator==(const _Self& __x) const { return _M_cur == __x._M_cur; } bool operator!=(const _Self& __x) const { return !(*this == __x); } bool operator<(const _Self& __x) const { return (_M_node == __x._M_node) ? (_M_cur < __x._M_cur) : (_M_node < __x._M_node); } bool operator>(const _Self& __x) const { return __x < *this; } bool operator<=(const _Self& __x) const { return !(__x < *this); } bool operator>=(const _Self& __x) const { return !(*this < __x); } void _M_set_node(_Map_pointer __new_node) { _M_node = __new_node; _M_first = *__new_node; _M_last = _M_first + difference_type(_S_buffer_size()); }至此,iterator的所有操作就结束了,看一下deque进行push_back、pop_back以及push_front和pop_front的操作是如何实现的。
void push_back(const value_type& __t) { if (_M_finish._M_cur != _M_finish._M_last - 1) {//如果cur还没有到最后一个可用的空间,可以直接使用 construct(_M_finish._M_cur, __t); ++_M_finish._M_cur; } else//已经到了最后一个可用空间,那么需要重新分配下一个空间了,因为finish.cur指向的是最后一个可用空间的下一个。 _M_push_back_aux(__t); }push_back如果空间不足的话,会调用_M_push_back_aux,其原型如下:
template <class _Tp, class _Alloc>void deque<_Tp,_Alloc>::_M_push_back_aux(const value_type& __t){ value_type __t_copy = __t; _M_reserve_map_at_back();//调整指针的位置 *(_M_finish._M_node + 1) = _M_allocate_node();//申请新的空间 __STL_TRY { construct(_M_finish._M_cur, __t_copy);//利用完最后一个空间 _M_finish._M_set_node(_M_finish._M_node + 1);//更新的参数 _M_finish._M_cur = _M_finish._M_first;//cur指向下一个可用的空间 } __STL_UNWIND(_M_deallocate_node(*(_M_finish._M_node + 1)));}_M_reserve_map_at_back()用于调整map的地址,原型如下:
void _M_reserve_map_at_back (size_type __nodes_to_add = 1) { if (__nodes_to_add + 1 > _M_map_size - (_M_finish._M_node - _M_map))//如果已经超出了当前的map可用空间了,需要重新分配 _M_reallocate_map(__nodes_to_add, false); }_M_reallocate_map函数主要是用来调整空间大小,首先需要申请一段空间,然后将原来的指针什么的都调整过去,再进行销毁,在这个过程中,应该只有finish和start的指针参数继续保持有效。
template <class _Tp, class _Alloc>void deque<_Tp,_Alloc>::_M_reallocate_map(size_type __nodes_to_add, bool __add_at_front){ size_type __old_num_nodes = _M_finish._M_node - _M_start._M_node + 1; size_type __new_num_nodes = __old_num_nodes + __nodes_to_add; _Map_pointer __new_nstart; if (_M_map_size > 2 * __new_num_nodes) {//如果剩余空间够用,就直接调整一下start和finish区间的位置 __new_nstart = _M_map + (_M_map_size - __new_num_nodes) / 2 + (__add_at_front ? __nodes_to_add : 0); if (__new_nstart < _M_start._M_node) copy(_M_start._M_node, _M_finish._M_node + 1, __new_nstart); else copy_backward(_M_start._M_node, _M_finish._M_node + 1, __new_nstart + __old_num_nodes); } else {//申请新的存储空间,然后将数据拷贝过去,并更新start和finish的值。 size_type __new_map_size = _M_map_size + max(_M_map_size, __nodes_to_add) + 2; _Map_pointer __new_map = _M_allocate_map(__new_map_size); __new_nstart = __new_map + (__new_map_size - __new_num_nodes) / 2 + (__add_at_front ? __nodes_to_add : 0); copy(_M_start._M_node, _M_finish._M_node + 1, __new_nstart); _M_deallocate_map(_M_map, _M_map_size); _M_map = __new_map; _M_map_size = __new_map_size; } _M_start._M_set_node(__new_nstart); _M_finish._M_set_node(__new_nstart + __old_num_nodes - 1);}
至此,push_back的所有操作基本都已经实现了。
push_front的实现:
void push_front(const value_type& __t) { if (_M_start._M_cur != _M_start._M_first) {//如果没有到做边界 construct(_M_start._M_cur - 1, __t); --_M_start._M_cur; } else _M_push_front_aux(__t); }
_M_push_front_aux函数原型如下:
template <class _Tp, class _Alloc>void deque<_Tp,_Alloc>::_M_push_front_aux(const value_type& __t){ value_type __t_copy = __t; _M_reserve_map_at_front();//调整map的头部 *(_M_start._M_node - 1) = _M_allocate_node();//申请新的空间 __STL_TRY { _M_start._M_set_node(_M_start._M_node - 1);//左移start _M_start._M_cur = _M_start._M_last - 1; construct(_M_start._M_cur, __t_copy); } __STL_UNWIND((++_M_start, _M_deallocate_node(*(_M_start._M_node - 1))));}_M_reserve_map_at_front函数原型如下:
void _M_reserve_map_at_front (size_type __nodes_to_add = 1) { if (__nodes_to_add > size_type(_M_start._M_node - _M_map)) _M_reallocate_map(__nodes_to_add, true);//增加map节点 }这部分的操作和上面push_back的操作还是基本类似的。
push_back和push_front的操作到这里基本就完成了,这一对操作基本类似,在增加数据之前,需要判断是否有超过左右边界,[start,finish),以及[fist,last)的开闭需要比较注意。所以在具体的实现的时候,略有不同。
与push_back和push_front相对的是pop_back和pop_front,这两个函数如下,可以对比来看:
pop_back()
void pop_back() { if (_M_finish._M_cur != _M_finish._M_first) { --_M_finish._M_cur; destroy(_M_finish._M_cur); } else _M_pop_back_aux(); }
pop_front()
void pop_front() { if (_M_start._M_cur != _M_start._M_last - 1) { destroy(_M_start._M_cur); ++_M_start._M_cur; } else _M_pop_front_aux(); }
这两个函数分别调用了_M_pop_back_aux和_M_pop_front_aux函数,其原型如下:
_M_pop_back_aux函数
void deque<_Tp,_Alloc>::_M_pop_back_aux(){ _M_deallocate_node(_M_finish._M_first); _M_finish._M_set_node(_M_finish._M_node - 1); _M_finish._M_cur = _M_finish._M_last - 1; destroy(_M_finish._M_cur);}
_M_pop_front_aux函数
template <class _Tp, class _Alloc>void deque<_Tp,_Alloc>::_M_pop_front_aux(){ destroy(_M_start._M_cur); _M_deallocate_node(_M_start._M_first); _M_start._M_set_node(_M_start._M_node + 1); _M_start._M_cur = _M_start._M_first;}这两个操作基本差不多,都是直接删除申请的存储空间,然后移动指针。
下面这个是clear()函数,用于清除整个deque。deque最初的状态是保留一个缓冲区,因此,clear之后恢复最初的状态,也是需要保留一个缓冲区的。 这里有一个疑问啊,如果一直使用pop_back或者pop_front是不是会导致空间全没有了,经过测试貌似还真是没有了额。。。这个待确认。。。
clear的函数原型如下:
template <class _Tp, class _Alloc> void deque<_Tp,_Alloc>::clear(){ for (_Map_pointer __node = _M_start._M_node + 1;//删除去头去尾的中间部分,这部分都是满的 __node < _M_finish._M_node; ++__node) { destroy(*__node, *__node + _S_buffer_size());//调用的destroy函数进行析构 _M_deallocate_node(*__node);//删除数据缓存节点 } if (_M_start._M_node != _M_finish._M_node) {//如果还有两个节点 destroy(_M_start._M_cur, _M_start._M_last);//只进行析构。。 destroy(_M_finish._M_first, _M_finish._M_cur); _M_deallocate_node(_M_finish._M_first);//只删除一个。 } else destroy(_M_start._M_cur, _M_finish._M_cur); _M_finish = _M_start;//调整状态}
erase操作,清除[first,last)区间内的所有元素。
template <class _Tp, class _Alloc>typename deque<_Tp,_Alloc>::iterator deque<_Tp,_Alloc>::erase(iterator __first, iterator __last){ if (__first == _M_start && __last == _M_finish) {//如果需要清除的是整个区间,那么就直接调用clear() clear(); return _M_finish; } else { difference_type __n = __last - __first;//清除区间的长度,由于有iterator,可以很方便的就知道删除那些空间了。。 difference_type __elems_before = __first - _M_start; if (__elems_before < difference_type((this->size() - __n) / 2)) {//清除数据的前方个数比较少 copy_backward(_M_start, __first, __last); iterator __new_start = _M_start + __n; destroy(_M_start, __new_start); _M_destroy_nodes(__new_start._M_node, _M_start._M_node); _M_start = __new_start; } else { copy(__last, _M_finish, __first); iterator __new_finish = _M_finish - __n; destroy(__new_finish, _M_finish); _M_destroy_nodes(__new_finish._M_node + 1, _M_finish._M_node + 1); _M_finish = __new_finish; } return _M_start + __elems_before; }}最后一个函数insert,很不容易啊。。。 直接看最简单的版本的insert,其实都差不多的,为了适应不同的输入参数。
template <class _Tp, class _Alloc>void deque<_Tp, _Alloc>::insert(iterator __pos, const value_type* __first, const value_type* __last) { size_type __n = __last - __first;//插入数据的个数 if (__pos._M_cur == _M_start._M_cur) {//在开头插入数据 iterator __new_start = _M_reserve_elements_at_front(__n);//调整之前数据空间 __STL_TRY { uninitialized_copy(__first, __last, __new_start);//将数据拷贝过去,因为有iterator,所以拷贝的时候可以当做是连续存储空间 _M_start = __new_start; } __STL_UNWIND(_M_destroy_nodes(__new_start._M_node, _M_start._M_node)); } else if (__pos._M_cur == _M_finish._M_cur) {//在末尾插入数据 iterator __new_finish = _M_reserve_elements_at_back(__n);//在增加数据空间 __STL_TRY { uninitialized_copy(__first, __last, _M_finish);//拷贝过去 _M_finish = __new_finish; } __STL_UNWIND(_M_destroy_nodes(_M_finish._M_node + 1, __new_finish._M_node + 1)); } else _M_insert_aux(__pos, __first, __last, __n);//调用标准函数}
_M_insert_aux函数原型如下:
template <class _Tp, class _Alloc>void deque<_Tp,_Alloc>::_M_insert_aux(iterator __pos, const value_type* __first, const value_type* __last, size_type __n){ const difference_type __elemsbefore = __pos - _M_start;//在pos之前的数据的个数 size_type __length = size(); if (__elemsbefore < __length / 2) {//如果前面的数据小于总的数据的一半,那么应该移动前面的数据 iterator __new_start = _M_reserve_elements_at_front(__n);//在前面增加n个缓存空间 iterator __old_start = _M_start; __pos = _M_start + __elemsbefore; __STL_TRY { if (__elemsbefore >= difference_type(__n)) { iterator __start_n = _M_start + difference_type(__n); uninitialized_copy(_M_start, __start_n, __new_start);// -----|----A---|-----B---|------C----|三段数据,先将B移到A,然后将C移到B起点的位置,最后将 _M_start = __new_start;//新的数据移动到剩下的位置。 copy(__start_n, __pos, __old_start); copy(__first, __last, __pos - difference_type(__n)); } else { const value_type* __mid = __first + (difference_type(__n) - __elemsbefore); __uninitialized_copy_copy(_M_start, __pos, __first, __mid, __new_start); _M_start = __new_start; copy(__mid, __last, __old_start); } } __STL_UNWIND(_M_destroy_nodes(__new_start._M_node, _M_start._M_node)); } else { iterator __new_finish = _M_reserve_elements_at_back(__n); iterator __old_finish = _M_finish; const difference_type __elemsafter = difference_type(__length) - __elemsbefore; __pos = _M_finish - __elemsafter; __STL_TRY { if (__elemsafter > difference_type(__n)) { iterator __finish_n = _M_finish - difference_type(__n); uninitialized_copy(__finish_n, _M_finish, _M_finish); _M_finish = __new_finish; copy_backward(__pos, __finish_n, __old_finish); copy(__first, __last, __pos); } else { const value_type* __mid = __first + __elemsafter; __uninitialized_copy_copy(__mid, __last, __pos, _M_finish, _M_finish); _M_finish = __new_finish; copy(__first, __mid, __pos); } } __STL_UNWIND(_M_destroy_nodes(_M_finish._M_node + 1, __new_finish._M_node + 1)); }}
到此位置,deque的基本原理差不多就完成了,有很多函数可能存在很多不同的参数方案,略有不同,但是大的思路是一致的,一切都是为了优化性能而实现的。deque算是比较复杂的数据组织方式,后面的stack、queue默认的底层容器就是deque,所以后面的stack、queue就直接跳过了,至于堆排序什么的,后面复习排序算法的时候,再一起进行算法总结吧。
- STL源码阅读-deque
- STL源码剖析---deque
- STL源码剖析---deque
- [STL]源码解析:deque
- STL源码剖析---deque
- STL源码剖析---deque
- STL源码剖析---deque
- STL源码分析--deque
- STL源码—deque
- STL源码—deque
- STL源码:deque
- STL源码剖析---deque
- STL源码剖析----deque
- STL源码剖析---deque
- STL之deque源码剖析
- STL 之 deque 源码剖析
- STL之deque源码剖析
- STL之deque源码剖析
- Oracle EBS OM 主要API示例
- 北汽为何弃A股赴H股
- 股市年关难过
- 白银将盘整
- PullToRefresh使用详解
- STL源码阅读-deque
- csdn博客又开始更新了
- C++编译错误C2365
- Oracle高级查询之CONNECT BY
- [LeetCode] Climbing Stairs
- 辛星和您一起解析PHP中的单例模式
- MFC的进程和线程
- XTUOJ 区间覆盖总长(线段树+离散化)
- ★ HDU 2853 求KM最大时,要求改动最少