STL略观——deque 的构造和内存管理constructor()、push_back() 和 push_front()
STL 所有容器应用到了空间配置器,当然 deque 在 _Deque_base 中设置了 两个空间配置器,一个负责缓冲区元素的空间配置,一个负责中控器map的指针空间配置:
typedef simple_alloc<_Tp, _Alloc> _Node_alloc_type; //负责缓冲区元素空间配置
typedef simple_alloc<_Tp*, _Alloc> _Map_alloc_type; //负责中控器map的指针空间配置
当然可以追溯下元素空间配置用的配置器是啥:
typedef typename _Base::allocator_type allocator_type; allocator_type get_allocator() const { return _Base::get_allocator(); }
咱们看看 _Base::get_allocator() 在哪里
typedef _Alloc allocator_type; allocator_type get_allocator() const { return allocator_type(); }
可以知道,其实 _Base::get_allocator() 还是默认的空间配置器 _Alloc;
deque 提供的 constructor 如下:
//拷贝构造函数
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); }
_Base(__a,__n)负责产生并安排好 deque 的结构,看一下_M_fill_initialized 在哪里,如下,该函数将元素的初值设定妥当:
template <class _Tp, class _Alloc> void deque<_Tp,_Alloc>::_M_fill_initialize(const value_type& __value) { _Map_pointer __cur; __STL_TRY {
//为每个节点的缓冲区设定初值 for (__cur = _M_start._M_node; __cur < _M_finish._M_node; ++__cur) { uninitialized_fill(*__cur, *__cur + _S_buffer_size(), __value); }
//最后一个节点的设定稍有不同(因为尾端可能有备用空间,不必设初值) uninitialized_fill(_M_finish._M_first, _M_finish._M_cur, __value); }
//失败就销毁已经配置好的 STL_UNWIND(destroy(_M_start, iterator(*__cur, __cur))); }
也看一下_Base(__a,__n)如何设定deque结构,根据 typedef _Deque_base<_Tp, _Alloc> _Base,找到_Deque_base构造函数如下:
_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); }
初始化_M_map等,这里就不多说了,找一下_M_initialize_map 函数
template <class _Tp, class _Alloc> void _Deque_base<_Tp,_Alloc>::_M_initialize_map(size_t __num_elements) { //节点数 = 元素总的个数 / 缓冲区大小 再 + 1; size_t __num_nodes = __num_elements / __deque_buf_size(sizeof(_Tp)) + 1; //中控器大小,最少为初始设定个数8个,最多“所需节点数 + 2” _M_map_size = max((size_t) _S_initial_map_size, __num_nodes + 2); //配置具有_M_map_size个节点的map中控器 _M_map = _M_allocate_map(_M_map_size); //设置头部和尾部 _Tp** __nstart = _M_map + (_M_map_size - __num_nodes) / 2; _Tp** __nfinish = __nstart + __num_nodes; __STL_TRY { //为中控器map中每个节点配置缓冲区 _M_create_nodes(__nstart, __nfinish); } //失败则撤销操作并释放空间 __STL_UNWIND((_M_deallocate_map(_M_map, _M_map_size), _M_map = 0, _M_map_size = 0)); //为deque内的两个迭代_M_start 和 _M_finish更新内容 _M_start._M_set_node(__nstart); _M_finish._M_set_node(__nfinish - 1); _M_start._M_cur = _M_start._M_first; //多配置一个节点,_M_cur指向多配置的节点起始处 _M_finish._M_cur = _M_finish._M_first + __num_elements % __deque_buf_size(sizeof(_Tp)); }
接下来是push_back()函数,push_back()函数首先判断在缓冲区是否有两个以上的元素备用空间,如果有则直接构造,没有就调用push_back_aux()函数,先配置一块新的缓冲区,然后设置新元素内容,然后更改迭代器 finish 状态:
void push_back(const value_type& __t) { //先判断是否有备用空间 if (_M_finish._M_cur != _M_finish._M_last - 1) { construct(_M_finish._M_cur, __t); ++_M_finish._M_cur; } else //没有就调用该函数进行配置新的空间,并设置finish状态 _M_push_back_aux(__t); }
咱们再来看一下_M_push_back() 和 _M_push_front()函数,_M_push_back()函数是当map尾部没有多余节点存储指向新的缓冲区的新指针的时候,需要额外在尾部继续开辟一个新的空间,来存放新的指针,同理,_M_push_front()函数在头部开辟新的空间来存储指针,当然要满足空间不足的前提。
//当map尾部还剩下一个节点(节点存取指向缓冲区的指针)时,就必须重新换一个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)) _M_reallocate_map(__nodes_to_add, false); } //同理,头部也是该操作 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很大,需要开辟额外的新的一块内存用来迁移map时,就会调用_M_reallocate_map来开辟新的内存,如下:
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; //旧的map的size size_type __new_num_nodes = __old_num_nodes + __nodes_to_add; //新的map的size _Map_pointer __new_nstart; if (_M_map_size > 2 * __new_num_nodes) //map目前的size如果大于两倍的新的size { __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 { size_type __new_map_size = _M_map_size + max(_M_map_size, __nodes_to_add) + 2; //配置一块空间,给map用 _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); //设定新map起始地点大小 _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); }