zoukankan      html  css  js  c++  java
  • stl源码分析之vector

    上篇简单介绍了gcc4.8提供的几种allocator的实现方法和作用,这是所有stl组件的基础,容器必须通过allocator申请分配内存和释放内存,至于底层是直接分配释放内存还是使用内存池等方法就不是组件需要考虑的事情。这篇文章开始分析gcc4.8 stl的容器源码实现。stl的容器分为序列式容器和关联式容器,前者包括vector,list,queue以及stack等常用数据结构,后者包含了map,set以及hash table等比较高级的结构,本文就从使用最广泛也是最基础的vector开始。

    一、 vector的定义

    vector继承自_Vector_base, _Vector_base包装了内存管理器_M_impl,下面是Vector_base的定义,由于代码过多,删除了一些不妨碍理解的源码:

    template<typename _Tp, typename _Alloc> 
        struct _Vector_base 
        { 
    //指定特定数据类型的内存分配器
          typedef typename __gnu_cxx::__alloc_traits<_Alloc>::template 
            rebind<_Tp>::other _Tp_alloc_type; 
          typedef typename __gnu_cxx::__alloc_traits<_Tp_alloc_type>::pointer 
            pointer; 
    
          struct _Vector_impl 
          : public _Tp_alloc_type 
          { 
           pointer _M_start;  //使用空间起始位置
            pointer _M_finish;  //使用空间结束位置
            pointer _M_end_of_storage;  //可使用空间结束位置 
            _Vector_impl() 
            : _Tp_alloc_type(), _M_start(0), _M_finish(0), _M_end_of_storage(0) 
            { } 
            _Vector_impl(_Tp_alloc_type const& __a) 
            : _Tp_alloc_type(__a), _M_start(0), _M_finish(0), _M_end_of_storage(0) 
            { } 
            _Vector_impl(_Tp_alloc_type&& __a)
            : _Tp_alloc_type(std::move(__a)),
              _M_start(0), _M_finish(0), _M_end_of_storage(0)
            { }
    //vector数据交换,只交换内存地址,不交换数据
            void _M_swap_data(_Vector_impl& __x) 
            { 
              std::swap(_M_start, __x._M_start); 
              std::swap(_M_finish, __x._M_finish); 
              std::swap(_M_end_of_storage, __x._M_end_of_storage); 
            } 
          }; 
     public: 
          _Vector_base() 
          : _M_impl() { } 
          _Vector_base(const allocator_type& __a) 
          : _M_impl(__a) { } 
          _Vector_base(size_t __n) 
          : _M_impl() 
          { _M_create_storage(__n); } 
          _Vector_base(size_t __n, const allocator_type& __a) 
          : _M_impl(__a) 
          { _M_create_storage(__n); } 
        _Vector_base(_Vector_base&& __x)    //移动构造函数,只交换3个指针,不copy数据
          : _M_impl(std::move(__x._M_get_Tp_allocator()))
          { this->_M_impl._M_swap_data(__x._M_impl); }
    //释放内存空间
          ~_Vector_base() 
          { _M_deallocate(this->_M_impl._M_start, this->_M_impl._M_end_of_storage 
                          - this->_M_impl._M_start); } 
        public: 
          _Vector_impl _M_impl; 
          pointer
        //通过 _M_impl申请内存
          _M_allocate(size_t __n) 
          { return __n != 0 ? _M_impl.allocate(__n) : 0; } 
          void 
       //通过 _M_impl释放内存
          _M_deallocate(pointer __p, size_t __n) 
          { 
            if (__p) 
              _M_impl.deallocate(__p, __n); 
          } 
        private: 
          void 
          _M_create_storage(size_t __n) 
          { 
            this->_M_impl._M_start = this->_M_allocate(__n); 
            this->_M_impl._M_finish = this->_M_impl._M_start; 
            this->_M_impl._M_end_of_storage = this->_M_impl._M_start + __n; 
          } 
        };

    Vector_base专门负责vector的内存管理,内部类_M_impl通过继承_Tp_alloc_type得到内存分配释放的功能,_M_allocate和_M_deallocate分别分配和释放vector所用内存,vector只需要负责元素构造和析构。

    rebind函数的原型是:

    template<typename _Tp1>  struct rebind 
            { typedef allocator<_Tp1> other; };

    用于得到特定类型的分配器,此处直接得到vector元素类型_Tp的分配器,其实并没有体现其作用,在list中我们将会看到它的技巧所在。

    vector继承于vector_base,具体内容后面讲解

    template<typename _Tp, typename _Alloc = std::allocator<_Tp> > 
        class vector : protected _Vector_base<_Tp, _Alloc> 
       { 
    …
    }

    默认使用 std::allocator内存分配器,即上篇文章介绍的new_allocator,简单封装了operator new和operator delete 。

    类结构图如下,

    二 、vector的迭代器

    vector使用的是__normal_iterator迭代器,

    typedef __gnu_cxx::__normal_iterator<pointer, vector> iterator; 
    typedef __gnu_cxx::__normal_iterator<const_pointer, vector> const_iterator;

    __normal_iterator定义于stl_iterator.h,封装了vector元素的指针,

    template<typename _Iterator, typename _Container> 
        class __normal_iterator 
        { 
        protected: 
          _Iterator _M_current; 
          typedef iterator_traits<_Iterator>                __traits_type; 
        public: 
      
          _GLIBCXX_CONSTEXPR __normal_iterator() : _M_current(_Iterator()) { } 
          explicit 
          __normal_iterator(const _Iterator& __i) : _M_current(__i) { } 
    //拷贝构造函数
          template<typename _Iter> 
            __normal_iterator(const __normal_iterator<_Iter, 
                              typename __enable_if< 
                   (std::__are_same<_Iter, typename _Container::pointer>::__value), 
                          _Container>::__type>& __i) 
            : _M_current(__i.base()) { } 
          reference 
          operator*() const 
          { return *_M_current; } 
          pointer 
          operator->() const 
          { return _M_current; } 
     __normal_iterator& 
          operator++() 
          { 
            ++_M_current; 
            return *this; 
          } 
          __normal_iterator 
          operator++(int) 
          { return __normal_iterator(_M_current++); } 
          __normal_iterator& 
          operator--() 
          { 
            --_M_current; 
            return *this; 
          } 
          __normal_iterator 
          operator--(int) 
          { return __normal_iterator(_M_current--); } 
          reference 
          operator[](const difference_type& __n) const 
          { return _M_current[__n]; } 
    
          __normal_iterator& 
          operator+=(const difference_type& __n) 
          { _M_current += __n; return *this; } 
          __normal_iterator 
          operator+(const difference_type& __n) const 
          { return __normal_iterator(_M_current + __n); } 
     __normal_iterator& 
          operator-=(const difference_type& __n) 
          { _M_current -= __n; return *this; } 
          __normal_iterator 
          operator-(const difference_type& __n) const 
          { return __normal_iterator(_M_current - __n); } 
          const _Iterator& 
          base() const 
          { return _M_current; } 
        };

    _M_current是指向迭代器位置的指针,这是一个随机访问型指针,operator+和operator-等移动操作可以直接移动到目的地,非随机访问型指针只能一步步移动。

    vector还会使用reverse_iterator,即逆序迭代器,顾名思义,其移动方向与普通迭代器相反

    explicit reverse_iterator(iterator_type __x) : current(__x) { }
      reference  operator*() const
          {
            _Iterator __tmp = current;
            return *--__tmp;  //取前一个位置上的值
          }
       reverse_iterator& operator++()
          {
            --current;
            return *this;
          }
        reverse_iterator& operator--()
          {
            ++current;
            return *this;
          }

    reverse_iterator的值并不在当前位置上,而是前一个位置上的值,++和--跟普通迭代器的移动方向正好相反。

    三、 vector的数据存储

    vector内存由_M_impl中的M_start,_M_finish,_M_end_of_storage三个指针管理,所有关于地址,容量大小等操作都需要用到这三个指针:

       iterator begin() _GLIBCXX_NOEXCEPT
          { return iterator(this->_M_impl._M_start); }
       iterator end() _GLIBCXX_NOEXCEPT
          { return iterator(this->_M_impl._M_finish); }
        reverse_iterator  rbegin() noexcept
          { return reverse_iterator(end()); }
        reverse_iterator rend() noexcept
          { return reverse_iterator(begin()); }
         size_type size() const _GLIBCXX_NOEXCEPT
          { return size_type(this->_M_impl._M_finish - this->_M_impl._M_start); }
        size_type capacity() const _GLIBCXX_NOEXCEPT
          { return size_type(this->_M_impl._M_end_of_storage
                             - this->_M_impl._M_start); }
       bool empty() const _GLIBCXX_NOEXCEPT
          { return begin() == end(); }

    结构图如下:

    _M_finish和_M_end_of_storage之间的空间没有数据,有时候这是一种浪费,c++11提供了一个很有用的函数shrink_to_fit(),将这段未使用空间释放,主要调用了下面代码,

    _Tp(__make_move_if_noexcept_iterator(__c.begin()),
                    __make_move_if_noexcept_iterator(__c.end()),
                    __c.get_allocator()).swap(__c);

    先把M_start,_M_finish之间的数据拷贝出来,再和原vector做一次swap,新的vector只包含M_start与_M_finish之间的元素,原vector空间被释放。

    四、 vector的构造方式

    vector提供了多种构造函数,

        vector() : _Base() { }  //使用默认内存分配器
        explicit vector(const allocator_type& __a)  //指定内存分配器
          : _Base(__a) { }
    //初始化为n个__value值,如果没指定就使用该类型默认值
        explicit vector(size_type __n, const value_type& __value = value_type(),
                 const allocator_type& __a = allocator_type())
          : _Base(__n, __a)
          { _M_fill_initialize(__n, __value); }
    //拷贝构造函数
        vector(const vector& __x)
          : _Base(__x.size(),
            _Alloc_traits::_S_select_on_copy(__x._M_get_Tp_allocator()))
          { this->_M_impl._M_finish =
              std::__uninitialized_copy_a(__x.begin(), __x.end(),
                                          this->_M_impl._M_start,
                                          _M_get_Tp_allocator());
          }
    //c++11的移动构造函数,获取__x的M_start,_M_finish,_M_end_of_storage,并不需要数据拷贝
      vector(vector&& ) noexcept
          : _Base(std::move(__x)) { }
    //从list中拷贝数据,也是c++11才有的
     vector(initializer_list<value_type> __l,
                 const allocator_type& __a = allocator_type())
          : _Base(__a)
          {
            _M_range_initialize(__l.begin(), __l.end(),
                                random_access_iterator_tag());
    //支持vector使用两个迭代器范围内的值初始化,除了stl的迭代器,也可以是数组地址
        template<typename _InputIterator,
                   typename = std::_RequireInputIter<_InputIterator>>
            vector(_InputIterator __first, _InputIterator __last,
                   const allocator_type& __a = allocator_type())
            : _Base(__a)
            { _M_initialize_dispatch(__first, __last, __false_type()); }
    //只析构所有元素,释放内存由vector_base完成
       ~vector() _GLIBCXX_NOEXCEPT
          { std::_Destroy(this->_M_impl._M_start, this->_M_impl._M_finish,
                          _M_get_Tp_allocator()); }

    五、 vector的元素操作

    vector的元素操作方式很丰富,这里只讨论insert函数,insert函数在指定位置前插入元素,可能需要移动数据和分配空间。

    typename vector<_Tp, _Alloc>::iterator
        vector<_Tp, _Alloc>::
        insert(iterator __position, const value_type& __x)
        {
          const size_type __n = __position – begin();
         //插入到最后一个位置,相当于push_back
          if (this->_M_impl._M_finish != this->_M_impl._M_end_of_storage
              && __position == end())
            {
              _Alloc_traits::construct(this->_M_impl, this->_M_impl._M_finish, __x);
              ++this->_M_impl._M_finish;
            }
          else
            {
                _M_insert_aux(__position, __x);
            }
          return iterator(this->_M_impl._M_start + __n);
        }
    
     template<typename _Tp, typename _Alloc>
        void
        vector<_Tp, _Alloc>::
        _M_insert_aux(iterator __position, const _Tp& __x)
        {
          if (this->_M_impl._M_finish != this->_M_impl._M_end_of_storage)
            { //内存空间足够
              _Alloc_traits::construct(this->_M_impl, this->_M_impl._M_finish,
                                       _GLIBCXX_MOVE(*(this->_M_impl._M_finish
                                                       - 1)));
              ++this->_M_impl._M_finish;
           //__position后的元素依次向后移动一个位置
              _GLIBCXX_MOVE_BACKWARD3(__position.base(),
                                      this->_M_impl._M_finish - 2,
                                      this->_M_impl._M_finish – 1);
           //目标地址赋值
              *__position = _Tp(std::forward<_Args>(__args)...);
            }
          else
            {
             //内存加倍
              const size_type __len =
                _M_check_len(size_type(1), "vector::_M_insert_aux");
              const size_type __elems_before = __position - begin();
              pointer __new_start(this->_M_allocate(__len));
              pointer __new_finish(__new_start);
              __try
                {
           //给position位置赋值
            _Alloc_traits::construct(this->_M_impl,
                                           __new_start + __elems_before,
                                           std::forward<_Args>(__args)...);
                                           __x);
                  __new_finish = 0;
          //拷贝position位置前元素
                  __new_finish
                    = std::__uninitialized_move_if_noexcept_a
                    (this->_M_impl._M_start, __position.base(),
                     __new_start, _M_get_Tp_allocator());
    
                  ++__new_finish;
               //拷贝position位置后元素
                  __new_finish
                    = std::__uninitialized_move_if_noexcept_a
                    (__position.base(), this->_M_impl._M_finish,
                     __new_finish, _M_get_Tp_allocator());
                }
              __catch(...)
                {
                  if (!__new_finish)
                    _Alloc_traits::destroy(this->_M_impl,
                                           __new_start + __elems_before);
                  else
                    std::_Destroy(__new_start, __new_finish, _M_get_Tp_allocator());
                  _M_deallocate(__new_start, __len);
                  __throw_exception_again;
                }
    
          //析构原vector所有元素
              std::_Destroy(this->_M_impl._M_start, this->_M_impl._M_finish,
                            _M_get_Tp_allocator());
           //释放原vector内存空间
              _M_deallocate(this->_M_impl._M_start,
                            this->_M_impl._M_end_of_storage
        this->_M_impl._M_start);
          //vector内存地址指向新空间
              this->_M_impl._M_start = __new_start;
              this->_M_impl._M_finish = __new_finish;
          this->_M_impl._M_end_of_storage = __new_start + __len;
            }
        }
    
    //内存分配策略并不是简单的加倍,如果n小于当前size,内存加倍,否则内存增长n。
       size_type
          _M_check_len(size_type __n, const char* __s) const
          {
            if (max_size() - size() < __n)
              __throw_length_error(__N(__s));
    
            const size_type __len = size() + std::max(size(), __n);
            return (__len < size() || __len > max_size()) ? max_size() : __len;
          }
  • 相关阅读:
    数据库基础理解学习-Mysql
    玫瑰花小制作分享-JavaScript(七夕专属浪漫)
    爬虫探索Chromedriver+Selenium初试
    Python 数据结构理解分享
    Python 实用第三方库安装方法
    Python安装-Pycharm+Anaconda
    oracle父子级查询数据树结构
    如何查看class文件的编译jdk版本号
    PL/SQL developer 11.0注册码
    linux 下tomcat出现 Native memory allocation (malloc) failed to allocate 1915224064 bytes for committing reserved memory问题
  • 原文地址:https://www.cnblogs.com/coderkian/p/3888429.html
Copyright © 2011-2022 走看看