C++进阶——STL源码之顺序容器vector

14 篇文章 4 订阅

顺序容器vector

vector的数据安排以及操作和array相似,二者的唯一的差别在于空间的运用灵活性:array 是静态空间,一旦配置之后就不能改变;vector是动态空间,随着元素的加入,它的内部机制会自行的扩充空间以容纳新的元素。

vector的实现技术,关键在于对其大小的控制以及重新配置时的数据移动效率,一旦vector旧空间满载,vector将会以一种空间配置策略来扩充空间,从而降低配置新空间、数据移动、释放旧空间带来的性能消耗。

vector类的结构图

通过上面的类结构图,可以知道类的成员变量定义在_Vector_impl(这个pointer 就是 value_type* )  中,以下为其源码:

 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) _GLIBCXX_NOEXCEPT
	: _Tp_alloc_type(__a), _M_start(0), _M_finish(0), _M_end_of_storage(0)
	{ }

#if __cplusplus >= 201103L
	_Vector_impl(_Tp_alloc_type&& __a) noexcept
	: _Tp_alloc_type(std::move(__a)),
	  _M_start(0), _M_finish(0), _M_end_of_storage(0)
	{ }
#endif

	void _M_swap_data(_Vector_impl& __x) _GLIBCXX_NOEXCEPT
	{
	  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);
	}
      };

其中定义的成员含义如下:

通过以下源码来理解上述成员:

      iterator
      begin() _GLIBCXX_NOEXCEPT
      { return iterator(this->_M_impl._M_start); }

      iterator
      end() _GLIBCXX_NOEXCEPT
      { return iterator(this->_M_impl._M_finish); }

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

vector的内存管理

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

_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) _GLIBCXX_NOEXCEPT
	: _Tp_alloc_type(__a), _M_start(0), _M_finish(0), _M_end_of_storage(0)
	{ }

#if __cplusplus >= 201103L
	_Vector_impl(_Tp_alloc_type&& __a) noexcept
	: _Tp_alloc_type(std::move(__a)),
	  _M_start(0), _M_finish(0), _M_end_of_storage(0)
	{ }
#endif

	void _M_swap_data(_Vector_impl& __x) _GLIBCXX_NOEXCEPT
	{
	  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:
      typedef _Alloc allocator_type;

      _Tp_alloc_type&
      _M_get_Tp_allocator() _GLIBCXX_NOEXCEPT
      { return *static_cast<_Tp_alloc_type*>(&this->_M_impl); }

      const _Tp_alloc_type&
      _M_get_Tp_allocator() const _GLIBCXX_NOEXCEPT
      { return *static_cast<const _Tp_alloc_type*>(&this->_M_impl); }

      allocator_type
      get_allocator() const _GLIBCXX_NOEXCEPT
      { return allocator_type(_M_get_Tp_allocator()); }

      _Vector_base()
      : _M_impl() { }

      _Vector_base(const allocator_type& __a) _GLIBCXX_NOEXCEPT
      : _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); }

#if __cplusplus >= 201103L
      _Vector_base(_Tp_alloc_type&& __a) noexcept
      : _M_impl(std::move(__a)) { }

      _Vector_base(_Vector_base&& __x) noexcept
      : _M_impl(std::move(__x._M_get_Tp_allocator()))
      { this->_M_impl._M_swap_data(__x._M_impl); }

      _Vector_base(_Vector_base&& __x, const allocator_type& __a)
      : _M_impl(__a)
      {
	if (__x.get_allocator() == __a)
	  this->_M_impl._M_swap_data(__x._M_impl);
	else
	  {
	    size_t __n = __x._M_impl._M_finish - __x._M_impl._M_start;
	    _M_create_storage(__n);
	  }
      }
#endif

      ~_Vector_base() _GLIBCXX_NOEXCEPT
      { _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_allocate(size_t __n)
      {
	typedef __gnu_cxx::__alloc_traits<_Tp_alloc_type> _Tr;
	return __n != 0 ? _Tr::allocate(_M_impl, __n) : 0;
      }

      void
      _M_deallocate(pointer __p, size_t __n)
      {
	typedef __gnu_cxx::__alloc_traits<_Tp_alloc_type> _Tr;
	if (__p)
	  _Tr::deallocate(_M_impl, __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;
      }
    };

下面将通过push_back的源码来了解vector的内存管理:

      void
      push_back(const value_type& __x)
      {
            //如果还有备用空间,则构造元素,并调整finish
            if (this->_M_impl._M_finish != this->_M_impl._M_end_of_storage)
           {
                _Alloc_traits::construct(this->_M_impl, this->_M_impl._M_finish,__x);
               ++this->_M_impl._M_finish;
          }
          else
                _M_insert_aux(end(), __x);//如果当前备用空间不足,调用_M_insert_aux
      }

_M_insert_aux的源码如下:

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;//调整finish的位置
        _Tp __x_copy = __x;

       //这里用于insert时,插入点后元素的移动处理,在push_back并无元素被操作
        _GLIBCXX_MOVE_BACKWARD3(__position.base(),
            this->_M_impl._M_finish - 2,
            this->_M_impl._M_finish - 1);
        * __position = __x_copy;
    }
    else
    {
        //获取当前实际配置大小,为当前size的2倍,源码见下方
        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
        {
            //在插入点构造元素,并赋予初值__x
            _Alloc_traits::construct(this->_M_impl,
                __new_start + __elems_before,
                __x);

            __new_finish = 0;

           //将之前的所有元素move到新的vector当中 
           __new_finish
                = std::__uninitialized_move_if_noexcept_a
                (this->_M_impl._M_start, __position.base(),
                    __new_start, _M_get_Tp_allocator());

            ++__new_finish;//更新finsh的位置

            //将插入点的原来的元素也copy到插入点的下一个位置
            __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());
        _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;
    }
}

_M_check_len的源码:

      size_type
      _M_check_len(size_type __n, const char* __s) const
      {
               //内存的分配是有限制的
               if (max_size() - size() < __n)
                   __throw_length_error(__N(__s));

              //__len为原size的2倍 
              const size_type __len = size() + std::max(size(), __n);
              return (__len < size() || __len > max_size()) ? max_size() : __len;
      }

_M_allocate的源码,调用_Vector_base中的内存分配函数:

      pointer
      _M_allocate(size_t __n)
      {
                typedef __gnu_cxx::__alloc_traits<_Tp_alloc_type> _Tr;
                return __n != 0 ? _Tr::allocate(_M_impl, __n) : 0;
      }

通过上面源码的分析,可以表示为如下的内存分配策略:

vector的元素操作

1. pop_back

      void
      pop_back() _GLIBCXX_NOEXCEPT
      {
             //前移finish,并析构finish指向的元素(前闭后开区间)
              --this->_M_impl._M_finish;
             _Alloc_traits::destroy(this->_M_impl, this->_M_impl._M_finish);
      }

2.  erase(iterator __position)调用了_M_erase,_M_erase的源码如下:

  template<typename _Alloc>
    typename vector<bool, _Alloc>::iterator
    vector<bool, _Alloc>::
    _M_erase(iterator __position)
    {
      //只是将删除位置之后的元素前移,然后更新finish位置,但是不知道为什么没有析构元素???
      if (__position + 1 != end())
        std::copy(__position + 1, end(), __position);
      --this->_M_impl._M_finish;
      return __position;
    }

 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值