STL略觀——deque 的構造和內存管理constructor()、push_back() 和 push_front()

  STL 全部容器應用到了空間配置器,固然 deque 在 _Deque_base 中設置了 兩個空間配置器,一個負責緩衝區元素的空間配置,一個負責中控器map的指針空間配置:node

  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() 在哪裏spa

  typedef _Alloc allocator_type;
  allocator_type get_allocator() const { return allocator_type(); }

  能夠知道,其實 _Base::get_allocator() 仍是默認的空間配置器 _Alloc;指針

  deque 提供的 constructor 以下:code

  //拷貝構造函數
 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 在哪裏,以下,該函數將元素的初值設定穩當:blog

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 函數element

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 狀態:get

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()函數在頭部開闢新的空間來存儲指針,固然要知足空間不足的前提。it

  //當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); }
相關文章
相關標籤/搜索