STL-分析vector扩容机制

总结下类型转换:

前言

最近被问到关于vector内存管理的一些问题,在暑假实习中遇到了用vector存储大量数据进行运算频繁使用push_back()时函数计算速度不理想的问题,当我改为使用普通的动态数组来代替时速度大大提升,之前并未细细研究其原因,只是猜个大概,不想面腾讯的时候被问到了,直接裂开…

查看源码
void push_back(_Ty&& _Val)
   	{	// insert by moving into element at end, provide strong guarantee
   	emplace_back(_STD move(_Val));
   	}

可以看到当我们去推入一个新元素时实际push_back还是调用emplace_back,它们有什么区别呢?

实际上:emplace_back是C++11后新加的一个代替push_back的函数,因为之前的push_back存在着临时变量申请资源的浪费,保留了push_back这个接口,但实际还是走的emplace_back。

跟进emplace_back
template<class... _Valty>
   	decltype(auto) emplace_back(_Valty&&... _Val)
   	{	// insert by perfectly forwarding into element at end, provide strong guarantee
   	if (_Has_unused_capacity())
   		{
   		return (_Emplace_back_with_unused_capacity(_STD forward<_Valty>(_Val)...));
   		}

   	_Ty& _Result = *_Emplace_reallocate(this->_Mylast(), _STD forward<_Valty>(_Val)...);
#if _HAS_CXX17
   	return (_Result);
#else /* ^^^ _HAS_CXX17 ^^^ // vvv !_HAS_CXX17 vvv */
   	(void)_Result;
#endif /* _HAS_CXX17 */
   	}

可以看到在emplace_back中,它首先会判断容器中是否还有未使用的容量(capacity),这里值得注意的是capacity(容量) >= size(元素个数)的,我们先暂且继续跟进如果未超过容量时

如果未超过容量时跟进_Emplace_back_with_unused_capacity
   template<class... _Valty>
   	decltype(auto) _Emplace_back_with_unused_capacity(_Valty&&... _Val)
   	{	// insert by perfectly forwarding into element at end, provide strong guarantee
   		// pre: _Has_unused_capacity()
   	_Alty_traits::construct(this->_Getal(), _Unfancy(this->_Mylast()), _STD forward<_Valty>(_Val)...);
   	_Orphan_range(this->_Mylast(), this->_Mylast());
   	_Ty& _Result = *this->_Mylast();
   	++this->_Mylast();
#if _HAS_CXX17
   	return (_Result);
#else /* ^^^ _HAS_CXX17 ^^^ // vvv !_HAS_CXX17 vvv */
   	(void)_Result;
#endif /* _HAS_CXX17 */
   	}

可以看到它的函数解释为当我们还有容量时,则在原数据的最后加上我们推入的新数据。那么当我们容量不够时呢,vector会怎么管理我们的数据和安排内存给它呢

如果未超过容量时跟进_Emplace_back_with_unused_capacity
   待更新
如果超过容量时跟进_Emplace_reallocate(加了些自己的理解)
   template<class... _Valty>
   	pointer _Emplace_reallocate(const pointer _Whereptr, _Valty&&... _Val)
   	{	// reallocate and insert by perfectly forwarding _Val at _Whereptr
   		// pre: !_Has_unused_capacity()
   		//_Whereptr是一个常量指针,指向最后一个数据的下标
   	const size_type _Whereoff = static_cast<size_type>(_Whereptr - this->_Myfirst());
   	_Alty& _Al = this->_Getal();
   	const size_type _Oldsize = size();
   	//如果原来的元素个数已经达到了一个极限,则提示已经太长
   	if (_Oldsize == max_size())
   		{
   		_Xlength();
   		}

   	const size_type _Newsize = _Oldsize + 1;
   	//计算新的容量应为多少,即扩容之后申请多大的内存
   	const size_type _Newcapacity = _Calculate_growth(_Newsize);
   	
   	//这里暂时不知道其作用是维护什么的,大概是一个新指针指向新的vector,并更新指向收尾的常数指针指向的值
   	const pointer _Newvec = _Al.allocate(_Newcapacity);
   	const pointer _Constructed_last = _Newvec + _Whereoff + 1;
   	pointer _Constructed_first = _Constructed_last;

   	//这里应该是指向了新的vector将要放新数据的地方,Whereoff前面是老数据的地方
   	_TRY_BEGIN
   	_Alty_traits::construct(_Al, _Unfancy(_Newvec + _Whereoff), _STD forward<_Valty>(_Val)...);
   	_Constructed_first = _Newvec + _Whereoff;
   	//这里判断了当前数组最后指向的是不是容器最后,可能存在容器未用完也再申请更大容量的情况?
   	if (_Whereptr == this->_Mylast())
   		{	// at back, provide strong guarantee
   		_Umove_if_noexcept(this->_Myfirst(), this->_Mylast(), _Newvec);
   		}
   	else
   		{	// provide basic guarantee
   		_Umove(this->_Myfirst(), _Whereptr, _Newvec);
   		_Constructed_first = _Newvec;
   		_Umove(_Whereptr, this->_Mylast(), _Newvec + _Whereoff + 1);
   		}
   	_CATCH_ALL
   	_Destroy(_Constructed_first, _Constructed_last);
   	_Al.deallocate(_Newvec, _Newcapacity);
   	_RERAISE;
   	_CATCH_END

   //三步骤:孤立所有迭代器,放弃旧的数组,获得新的数组
   	_Change_array(_Newvec, _Newsize, _Newcapacity);
   	return (this->_Myfirst() + _Whereoff);
   	}

我们先分析每次扩容的容量是怎样计算的

扩容分析_Calculate_growth
size_type _Calculate_growth(const size_type _Newsize) const
   	{	// given _Oldcapacity and _Newsize, calculate geometric growth
   	const size_type _Oldcapacity = capacity();

   	if (_Oldcapacity > max_size() - _Oldcapacity / 2)
   		{
   		return (_Newsize);	// geometric growth would overflow
   		}

   	const size_type _Geometric = _Oldcapacity + _Oldcapacity / 2;

   	if (_Geometric < _Newsize)
   		{
   		return (_Newsize);	// geometric growth would be insufficient
   		}

   	return (_Geometric);	// geometric growth is sufficient
   	}

可以看到它会优先判断我们的旧vector是不是已经很长了,当长再有1/2个当前容器容量就到最大值时,我们的新vector一次扩容容量只会+1,而正常情况下每次扩容容量是原容器容量的1.5倍(在GCC编译器中是2倍),而当计算的新的容量<元素个数时,会将元素个数返回,例如原来容量为1,1.5倍是1.5,显然是小于你推入了一个数据长度变成2的size的。

尽管还有一些没完全明白的具体操作步骤,但vector的扩容机制已经很明白了。

注意

该文章仅个人学习使用,欢迎大家一起交流学习

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
回答: 当Vector集合的元素数量超过其当前容量时,Vector会自动进行扩容扩容的具体过程如下:首先,Vector会创建一个新的数组,其大小通常是当前容量的两倍。然后,Vector会将原数组中的元素复制到新数组中。最后,Vector会将新数组设置为其内部的数组。这样,Vector就完成了扩容。\[1\] 在Java中,Vector的默认初始容量是10。当向一个默认初始容量为10的Vector中添加第11个元素时,就会触发扩容机制扩容后,Vector的容量会增加到20。\[1\] 在C++中,使用STL标准模板类vector时,可以使用reserve()函数来保证容器的最小容量。当容器的元素数量超过其当前容量时,vector会自动进行扩容扩容的具体过程如下:首先,vector会创建一个新的数组,其大小通常是当前容量的两倍。然后,vector会将原数组中的元素复制到新数组中。最后,vector会将新数组设置为其内部的数组。这样,vector就完成了扩容。\[2\] 无论是在Java中的Vector还是在C++中的vector,它们都会根据需要自动进行扩容,以容纳更多的元素。\[1\]\[2\] #### 引用[.reference_title] - *1* *3* [Vector扩容机制,附源码解释](https://blog.csdn.net/qq_52652488/article/details/126252775)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [vector扩容](https://blog.csdn.net/qq_60669466/article/details/124061431)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

宇智波盆

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值