std::string reserve详解

详解当然是从源码入手了.

我们先来看看string的reserve的源码

  template<typename _CharT, typename _Traits, typename _Alloc>
    void
    basic_string<_CharT, _Traits, _Alloc>::
    reserve(size_type __res)
    {
      if (__res < length())
	__res = length();

      const size_type __capacity = capacity();
      if (__res != __capacity)
	{
	  if (__res > __capacity
	      || __res > size_type(_S_local_capacity))
	    {
	      pointer __tmp = _M_create(__res, __capacity);
	      this->_S_copy(__tmp, _M_data(), length() + 1);
	      _M_dispose();
	      _M_data(__tmp);
	      _M_capacity(__res);
	    }
	  else if (!_M_is_local())
	    {
	      this->_S_copy(_M_local_data(), _M_data(), length() + 1);
	      _M_destroy(__capacity);
	      _M_data(_M_local_data());
	    }
	}
    }

首先看这三句

      if (__res < length())
	__res = length();

      const size_type __capacity = capacity();

首先 _res 是我们的传进来的参数 先与我们string的长度对比 小于的话设置为长度 这是为了不使得string收缩 然后得到一个capacity的值

然后是一个判断语句

	  if (__res > __capacity
	      || __res > size_type(_S_local_capacity))

因为我们上面把_res最小值已经设置为length 所以如果_res大于capacity意味着要进行扩容 但也有可能现在的数据还存放在local_capacity中 什么是local_capacity呢

      enum { _S_local_capacity = 15 / sizeof(_CharT) };

      union
      {
	_CharT           _M_local_buf[_S_local_capacity + 1];
	size_type        _M_allocated_capacity;
      };

首先_S_local_capacity大小基于_CharT _CharT是string的字符类型可能为char wchar_t u16char_t u32char_t string中初始的数据如果小于_S_local_capacity 后两个C++11以后支持 则存放在_M_local_buf中 大于的话进行扩容 存放在又一个alloctor申请的空间中 存储的形式是一个裸指针

我们再回到刚刚的判断条件的第二部 如果大于初始的容量 我们都会进行扩容

pointer __tmp = _M_create(__res, __capacity);
this->_S_copy(__tmp, _M_data(), length() + 1);

我们现在来到了这个函数最重要的部分

我们首先看第一步 即已传入的参数的大小进行扩容 如果其大于本身的length 则是参数多大就扩容多大 _M_create的参数第一个是new_capacity 第二个是old_capacity, 然后是第二步 _M_data()会返回原来的指针 所以这一步的作用是拷贝数据 把原数据拷贝到先指针上

_M_dispose();
_M_data(__tmp);
_M_capacity(__res);

这三个函数比较简单 第一个是销毁原指针 第二个是把申请的指针付给string对象 第三个是设置新容量 因为_M_create_的第一个参数是引用 所以_res会在一些条件下改变 条件如下:

      if (__capacity > __old_capacity && __capacity < 2 * __old_capacity)
	{
	  __capacity = 2 * __old_capacity;
	  if (__capacity > max_size())
	    __capacity = max_size();
	}

我们再来看最后一个判断条件及其内容

	  else if (!_M_is_local())
	    {
	      this->_S_copy(_M_local_data(), _M_data(), length() + 1);
	      _M_destroy(__capacity);
	      _M_data(_M_local_data());
	    }

我们前面说道在capacity小于_S_local_capacity时我们的数据其实存放在栈中 即一个字符数组中 这一步其实就是当我们现在的length小于_S_local_capacity且其数据存储在alloctor分配的内存中 这个时候我们要做的是把数据转移到_M_local_buf中 即栈中, 然后销毁alloctor分配的内存 然后就是重新设置真实存储数据的指针为_M_local_buf (_M_local_data()返回_M_local_buf)

这样对于reserve这个函数我们算是无死角的理解了

posted @ 2022-07-02 13:18  李兆龙的博客  阅读(292)  评论(0编辑  收藏  举报