zoukankan      html  css  js  c++  java
  • C++ Standard Stl SGI STL源码学习笔记(04) stl_deque && 初涉STL内存管理

      在SGI STL源码中,很多时候一些模板类提供默认形参的时候,都会选择提供deque.

    template <class _Tp, 
              class _Sequence __STL_DEPENDENT_DEFAULT_TMPL(deque<_Tp>) >    // stl_stack.h
    

     

    template <class _Tp, 
              class _Sequence __STL_DEPENDENT_DEFAULT_TMPL(deque<_Tp>) >    // stl_queue.h  

     

    _STL_DEPENDENT_DEFAULT_TMPL宏就是替换后扩展为"=deque<_Tp>".既然出场的频率那么高,所以我首先选择他.一起去看看deque的源码.

    deque的源码很多,一千六百多行的代码. 而且实现也是蛮复杂的.包括deque_iterator,deque_allocator,deque_base,deque.如果你也在阅读deque的源码,

    我的建议是先从deque开始阅读源码,了解成员模板函数的功能.  然后再慢慢向上扩展,到迭代器和内存管理.

      还有一点,我的建议是慢慢的迭代,纵向发展,而不是横向.除非迫不得已,一般我还是选择纵向发展.

    1.  好吧,题外话说了不少了,现在一起阅读源码.  首先先从deque中的功能函数下手,虽然这些函数很简单.对,我们就是从最简单的下手.

      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 front() { return *_M_start; }
      
      reference back() {
        iterator __tmp = _M_finish;
        --__tmp;
        return *__tmp;
      }
      const_reference front() const { return *_M_start; }
      const_reference back() const {
        const_iterator __tmp = _M_finish;
        --__tmp;
        return *__tmp;
      }
    
      size_type size() const { return _M_finish - _M_start; }
      size_type max_size() const { return size_type(-1); }
      bool empty() const { return _M_finish == _M_start; }
    

          我想这些简单的函数还是很好明白的.

    2.   接下来该从哪里下手呢?  很多时候面对源码不知道该从什么地方开始,这就是为什么有些人阅读源码畅通无阻,有些人则很吃力.是因为没有使用

      合适的方法. 为什么不去看看deque的成员变量呢?

      成员变量都是从_Deque_base继承过来的,很容易找得到,找到swap函数就找到了.

      

     void swap(deque& __x) {
        __STD::swap(_M_start, __x._M_start);
        __STD::swap(_M_finish, __x._M_finish);
        __STD::swap(_M_map, __x._M_map);
        __STD::swap(_M_map_size, __x._M_map_size);
      }
    

      

    3.接下来我就不再说的那么啰嗦了,也不会再设计到如何阅读源码和详细说明阅读方法的问题了.我们直奔主题吧~

     首先来看看deque的设计吧~

        deque继承_Deque_base, _Deque_base是骨架,deque派生类只是增加STL提供给我们的封装操作API.

        

        _Deque_base的实现由两个版本,由__STL_USE_STD_ALLOCATORS宏控制. 

        与_Deque_base相关的是_Deque_Alloc_base.这个模板类也是由__STL_USE_STD_ALLOCATORS控制,而且是和_Deque_base在一起.

        deque中的迭代器功能来自于_Deque_iterator.

     好吧,现在在源码中出现的类都讲完了. 串一下,deque的功能由上面几个不同的组件拼合而成.

      

    4.看一看STL的内存分配.

        由于宏__STL_USE_STD_ALLOCATORS的控制可以使用两个版本的_Deque_alloc_base.所以我们去阅读提供了默认分配策略的那个.

    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); }
    

      simple_alloc就是提供的默认分配策略,allocate与deallocate提供内存的管理策略.  再看另外一个。

    template <class _Tp, class _Alloc, bool __is_static>
    class _Deque_alloc_base {
    public:
      typedef typename _Alloc_traits<_Tp,_Alloc>::allocator_type allocator_type;
      allocator_type get_allocator() const { return _M_node_allocator; }
    
      _Deque_alloc_base(const allocator_type& __a)
        : _M_node_allocator(__a), _M_map_allocator(__a),
          _M_map(0), _M_map_size(0)
      {}
      
    protected:
      typedef typename _Alloc_traits<_Tp*, _Alloc>::allocator_type
              _Map_allocator_type;
    
      allocator_type      _M_node_allocator;
      _Map_allocator_type _M_map_allocator;
    
      _Tp* _M_allocate_node() {
        return _M_node_allocator.allocate(__deque_buf_size(sizeof(_Tp)));
      }
      void _M_deallocate_node(_Tp* __p) {
        _M_node_allocator.deallocate(__p, __deque_buf_size(sizeof(_Tp)));
      }
      _Tp** _M_allocate_map(size_t __n) 
        { return _M_map_allocator.allocate(__n); }
      void _M_deallocate_map(_Tp** __p, size_t __n) 
        { _M_map_allocator.deallocate(__p, __n); }
    

      

     看一下_Alloc_traits文件源码:

    template <class _Tp, class _Tp1>
    struct _Alloc_traits<_Tp, allocator<_Tp1> >
    {
      static const bool _S_instanceless = true;
      typedef simple_alloc<_Tp, alloc> _Alloc_type;
      typedef allocator<_Tp> allocator_type;
    };
    
    // Versions for the predefined SGI-style allocators.
    
    template <class _Tp, int __inst>
    struct _Alloc_traits<_Tp, __malloc_alloc_template<__inst> >
    {
      static const bool _S_instanceless = true;
      typedef simple_alloc<_Tp, __malloc_alloc_template<__inst> > _Alloc_type;
      typedef __allocator<_Tp, __malloc_alloc_template<__inst> > allocator_type;
    };
    
    template <class _Tp, bool __threads, int __inst>
    struct _Alloc_traits<_Tp, __default_alloc_template<__threads, __inst> >
    {
      static const bool _S_instanceless = true;
      typedef simple_alloc<_Tp, __default_alloc_template<__threads, __inst> > 
              _Alloc_type;
      typedef __allocator<_Tp, __default_alloc_template<__threads, __inst> > 
              allocator_type;
    };
    
    template <class _Tp, class _Alloc>
    struct _Alloc_traits<_Tp, debug_alloc<_Alloc> >
    {
      static const bool _S_instanceless = true;
      typedef simple_alloc<_Tp, debug_alloc<_Alloc> > _Alloc_type;
      typedef __allocator<_Tp, debug_alloc<_Alloc> > allocator_type;
    };
    
    // Versions for the __allocator adaptor used with the predefined
    // SGI-style allocators.
    
    template <class _Tp, class _Tp1, int __inst>
    struct _Alloc_traits<_Tp, 
                         __allocator<_Tp1, __malloc_alloc_template<__inst> > >
    {
      static const bool _S_instanceless = true;
      typedef simple_alloc<_Tp, __malloc_alloc_template<__inst> > _Alloc_type;
      typedef __allocator<_Tp, __malloc_alloc_template<__inst> > allocator_type;
    };
    
    template <class _Tp, class _Tp1, bool __thr, int __inst>
    struct _Alloc_traits<_Tp, 
                          __allocator<_Tp1, 
                                      __default_alloc_template<__thr, __inst> > >
    {
      static const bool _S_instanceless = true;
      typedef simple_alloc<_Tp, __default_alloc_template<__thr,__inst> > 
              _Alloc_type;
      typedef __allocator<_Tp, __default_alloc_template<__thr,__inst> > 
              allocator_type;
    };
    
    template <class _Tp, class _Tp1, class _Alloc>
    struct _Alloc_traits<_Tp, __allocator<_Tp1, debug_alloc<_Alloc> > >
    {
      static const bool _S_instanceless = true;
      typedef simple_alloc<_Tp, debug_alloc<_Alloc> > _Alloc_type;
      typedef __allocator<_Tp, debug_alloc<_Alloc> > allocator_type;
    };
    

      源码中显示了模板_Alloc_traits以及很多的偏特化版本. 我们发现实际的内存管理功能是 _allocator和simple_alloc提供的.

       主要有下面几个模板类:__malloc_alloc_template,

                  __default_alloc_template,

                  debug_alloc. 

      去看看__malloc_alloc_template的实现.

      

      static void* allocate(size_t __n)
      {
        void* __result = malloc(__n);
        if (0 == __result) __result = _S_oom_malloc(__n);
        return __result;
      }
    
      static void deallocate(void* __p, size_t /* __n */)
      {
        free(__p);
      }
    
      static void* reallocate(void* __p, size_t /* old_sz */, size_t __new_sz)
      {
        void* __result = realloc(__p, __new_sz);
        if (0 == __result) __result = _S_oom_realloc(__p, __new_sz);
        return __result;
      }
    

      可见底层对于内存的管理是通过malloc和realloc,free等标准库函数实现的.

    5. 我们讨论一下_Alloc_traits存在的意义.

      昨天我看了一下C++ traits技术,这里有资料链接:

        Traits: a new and useful template technique by Nathan C. Myers

            http://www.cantrip.org/traits.html

      

       很多人讨论C++ traits的时候也习惯成文C++ 萃取技术. 使用_Alloc_traits使对于allocator的使用变得更加灵活,

       Container根本不必知道底层的Allocator到底是什么样子的,而是只需要知道他们模糊的样子_Alloc_traits.因为他们

         只需要知道内存申请和释放的接口.其他的不关心,同时也没有必要知道.

    6.就刚才上面发现如果内存申请或者是重新申请失败的时候就会调用_S_oom_realloc和_S_oom_malloc方法.不妨去看看SGI STL是怎么处理这种内存

    申请失败的情况的.

      

    template <int __inst>
    void*
    __malloc_alloc_template<__inst>::_S_oom_malloc(size_t __n)
    {
        void (* __my_malloc_handler)();      //函数指针. 1
        void* __result;
    
        for (;;) {
            __my_malloc_handler = __malloc_alloc_oom_handler;  //handler 2
            if (0 == __my_malloc_handler) { __THROW_BAD_ALLOC; }
            (*__my_malloc_handler)();
            __result = malloc(__n);
            if (__result) return(__result);
        }
    }
    
    template <int __inst>
    void* __malloc_alloc_template<__inst>::_S_oom_realloc(void* __p, size_t __n)
    {
        void (* __my_malloc_handler)();
        void* __result;
    
        for (;;) {
            __my_malloc_handler = __malloc_alloc_oom_handler;
            if (0 == __my_malloc_handler) { __THROW_BAD_ALLOC; }
            (*__my_malloc_handler)();
            __result = realloc(__p, __n);
            if (__result) return(__result);
        }
    }
    

      

     看到上面的源码,是通过在for循环中通过回调实现的.而在__malloc_alloc_template也暴露出一个设置Handler的方法.

     static void (* __set_malloc_handler(void (*__f)()))()  // 这种方式看上去好别扭. 
      {
        void (* __old)() = __malloc_alloc_oom_handler;
        __malloc_alloc_oom_handler = __f;
        return(__old);
      }
    

     

    我在上面注明了,这种方式看上去好别扭,暴露出的接口为什么也要声明成为函数指针的形式.

    而本身对Handler的限制是很简单的,甚至都没有参数限制.

      static void (* __malloc_alloc_oom_handler)();
    

      

     我只是理清思路,做到在源码中畅游无阻. 剩下的就是关心那部分的实现,或者是对哪种细节的技术感兴趣.然后针对个人情况去处理.

     欢迎对细节提出问题讨论. 技术是讨论出来的~ 另外:欢迎拍砖~

          

          

     
  • 相关阅读:
    GCC内置函数
    父类子类的拷贝构造与赋值
    外传三 动态内存申请的结果
    外传二 函数的异常规格说明
    外传一 异常处理深度解析
    第69课 技巧,自定义内存管理
    第68课 拾遗,令人迷惑的写法
    第67课 经典问题解析五
    第66课 C++中的类型识别
    第65课 C++中的异常处理(下)
  • 原文地址:https://www.cnblogs.com/respawn/p/2613076.html
Copyright © 2011-2022 走看看