zoukankan      html  css  js  c++  java
  • 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); }
    既然选择了远方,便只顾风雨兼程
  • 相关阅读:
    算法笔记--二分图判定
    算法笔记--最小表示法
    Codeforces 525A
    Codeforces 140D
    Codeforces C
    Codeforces 665C
    Codeforces 604B
    Codeforces 285C
    The Fewest Coins POJ
    Triangular Pastures POJ
  • 原文地址:https://www.cnblogs.com/Forever-Road/p/6838203.html
Copyright © 2011-2022 走看看