• C++ Standard Stl SGI STL源码学习笔记(05) stl_vector 与 一些问题的细化 1


      上篇文章中很粗略的角度讲解了一下stl_deque的设计思想,以及涉及到得浅显的STL内存管理方面,至少我们看得到的冰山一角.

      这篇文章中关于vector的分析,我将将一些问题细化一下,对一些函数做细致的分析.有些时候,有些问题还是说清楚比较好.

      打开stl_vector的源码,发现vector的设计思路和stl_deque如出一辙,想想这样是很合理的,保持实现的一致性.只是stl_vector没有提供一个确定的

    模板类Iterator去实现迭代器,而是在vector模板类中实现了迭代. 但是和deque基本没有什么大的不同.下面来看看源码:

      

      1. 这次就不讲解设计部分了,主要讲解实现部分.如果没有看前面一篇文章,我尽力做到和前一篇文章没有关系,但是最好还是去了解一下设计部分.详细

        可参考这篇文章:C++ Standard Stl -- SGI STL源码学习笔记(04) stl_deque && 初涉STL内存管理.

      2. vector的内存管理模板类:_Vector_alloc_base. 它是一个模板类,但是有一个特化的版本,区别在于对分配器Allocator(不知道这种分配器的叫法

        是否合适,以前阅读apache源码分析的时候,里面对于allocator称作分配子,但是这两者的作用和概念都是不同的.)对象是否为成员变量.

        好吧,对于通用_Vector_alloc_base模板类和特化_Vector_alloc_base模板类,我们从内存操作的方法上面区分一下:

        通用_Vector_alloc_base模板内存操作方法: 

      _Tp* _M_allocate(size_t __n)              // 使用成员变量实现内存操作  
        { return _M_data_allocator.allocate(__n); }
      void _M_deallocate(_Tp* __p, size_t __n)
        { if (__p) _M_data_allocator.deallocate(__p, __n); }
    

         特化_Vector_alloc_base内存操作方法:

      typedef typename _Alloc_traits<_Tp, _Allocator>::_Alloc_type _Alloc_type;  // 使用方法直接操作,没有作为成员变量
      _Tp* _M_allocate(size_t __n)
        { return _Alloc_type::allocate(__n); }
      void _M_deallocate(_Tp* __p, size_t __n)
        { _Alloc_type::deallocate(__p, __n);}
    

         当然,无论是通用模板类还是特化模板类,都使用了traits技术.

      3. vector的成员变量:   

      _Tp* _M_start;      // start
      _Tp* _M_finish;      // 初始化的时候_M_finish = _M_start
      _Tp* _M_end_of_storage  // 指向最后一个元素的下一个位置. 注意不是最后一个元素.
    

        这三个成员变量都是vector继承自基类, 这三个指针指向三个不同的位置. 可以从构造函数中看得到:    

    _Vector_base(size_t __n, const _Alloc&)
        : _M_start(0), _M_finish(0), _M_end_of_storage(0) 
      {
        _M_start = _M_allocate(__n);
        _M_finish = _M_start;
        _M_end_of_storage = _M_start + __n;
      }
    

        根据初始化的长度_n初始化_M_start, _M_finish, _M_end_of_storage.  

      explicit vector(const allocator_type& __a = allocator_type())
        : _Base(__a) {}
    
      vector(size_type __n, const _Tp& __value,
             const allocator_type& __a = allocator_type()) 
        : _Base(__n, __a)
        { _M_finish = uninitialized_fill_n(_M_start, __n, __value); }
    
      explicit vector(size_type __n)
        : _Base(__n, allocator_type())
        { _M_finish = uninitialized_fill_n(_M_start, __n, _Tp()); }
    
      vector(const vector<_Tp, _Alloc>& __x) 
        : _Base(__x.size(), __x.get_allocator())
        { _M_finish = uninitialized_copy(__x.begin(), __x.end(), _M_start); }
    

       由vector提供的构造函数可以看得出,都是使用基类_vector_base的构造函数进行初始化.由于vector模板类提供了一个默认参数,可以使用默认

       分配器allocator.

    inline T* allocate(ptrdiff_t size, T*) {
        set_new_handler(0);
        T* tmp = (T*)(::operator new((size_t)(size * sizeof(T))));
        if (tmp == 0) {
    	cerr << "out of memory" << endl; 
    	exit(1);
        }
        return tmp;
    }
    

       初始化后M_start得到了allocate函数的返回指针,而M_finish默认初始化和M_start相同.看看对于_M_finish的处理.

       由宏uninitialized_fill_n处理,进行跳转,到__uninitialized_fill_n,再到__uninitialized_fill_n_aux.

       SGI STL在这里做了相当复杂的处理,主要是判断vector里面的元素类型判断,是POD类型还是不是POD类型.(POD? Plain Old Data,朴素的旧式数据,built-in类型就是POD

       可以参开<<c++必知必会>> 条款11 编译期会在类中放东西. 其中讲到了类多态中的v-table,考虑是不是写一篇随笔介绍一下.)

    template <class _ForwardIter, class _Size, class _Tp, class _Tp1>
    inline _ForwardIter 
    __uninitialized_fill_n(_ForwardIter __first, _Size __n, const _Tp& __x, _Tp1*)
    {
      typedef typename __type_traits<_Tp1>::is_POD_type _Is_POD;
      return __uninitialized_fill_n_aux(__first, __n, __x, _Is_POD());
    }
    

      _type_traits模板类提供了很多偏特化,都是处理这种build-in类型. 对于build-in POD is_POD_type都是_true_type.

    template <class _ForwardIter, class _Size, class _Tp>
    inline _ForwardIter
    __uninitialized_fill_n_aux(_ForwardIter __first, _Size __n,
                               const _Tp& __x, __true_type)
    {
      return fill_n(__first, __n, __x);
    }
    
    template <class _ForwardIter, class _Size, class _Tp>
    _ForwardIter
    __uninitialized_fill_n_aux(_ForwardIter __first, _Size __n,
                               const _Tp& __x, __false_type)
    {
      _ForwardIter __cur = __first;
      __STL_TRY {
        for ( ; __n > 0; --__n, ++__cur)
          _Construct(&*__cur, __x);
        return __cur;
      }
      __STL_UNWIND(_Destroy(__first, __cur));
    }
    

      这里是对_M_finish的最终实现.补充fill_n的源码: 

    template <class _OutputIter, class _Size, class _Tp>
    _OutputIter fill_n(_OutputIter __first, _Size __n, const _Tp& __value) {
      __STL_REQUIRES(_OutputIter, _OutputIterator);
      for ( ; __n > 0; --__n, ++__first)
        *__first = __value;
      return __first;
    }
    

      不管vector中的元素类型是否是POD,都是移动_M_start,而且还做了相应的初始化.

     

     
  • 相关阅读:
    MYSQL导入,导出命令。
    MySQL修改,表结构大幅修改
    Ajax
    js和jQuery的日常
    freemarker 分页取值
    Timer定时任务
    汉字相似度比较
    读取Properties键值对
    Python+requests+unittest+excel实现接口自动化测试框架
    Android App 压力测试方法(Monkey)
  • 原文地址:https://www.cnblogs.com/respawn/p/2613774.html
Copyright © 2020-2023  润新知