《C/C++ 面试 100 例》(五)string 扩容策略

一、引例

1、string 扩容概述

  • string 就是动态字符数组,一旦出现 ‘动态’ 二字,就不可能一开始就申请很大的内存,一定有它内部的申请策略;
  • vector 的动态扩容策略可以参考我之前写的一篇博客:
    C++ vector 扩容策略.

2、扩容尝试

  • 通过 VS2013 环境下,string 在一个一个插入元素 (push_back)的过程中,size 和 capacity 的对应关系如下;
sizecapacity
015
115
15
1415
1515
1631
1731
31
3031
3131
3247
3347
  • 观察发现,只要 string 这个对象创建出来了,无论有没有元素,就占用了15个字节,当 size 达到 capacity 时,又增加了 16 个字节;

二、扩容逻辑猜测

  • 声明:因为我的环境上没有找到 string 的源码, push_back 的断点也没法跟进去,所以只能猜了;

1、猜测一:常数增量

  • 目前观察得到的 capacity 的序列为:
	15 -> 31 -> 47
  • 猜测 string 的增长策略为每次 capacity 不足时,采取增加 16 个字节的方案;
  • 因为数据太少没有说服力,所以我们准备更多数据,再来看看情况如何:
sizecapacitycapacity - oldcapacity
0150
163116
324716
487023
7110535
10615752
15823578
236352117
353528176
529792264
7931188396
11891782594
17832673891
267440091336
401060132004
601490193006
9020135284509
  • 当某次 size > capacity 时,增长的 capacity 增量并不是一个常数,所以 猜测一 被推翻;

2、猜测二:倍数增量

  • 猜测是倍增的策略以后,我们就要看这个倍增因子了,将每次 size > capacity 时,两次 capacity 相除,得到如下的表:
sizecapacitycapacity / oldcapacity
015/
16312.066667
32471.516129
48701.489362
711051.500000
1061571.495238
1582351.496815
2363521.497872
3535281.500000
5297921.500000
79311881.500000
118917821.500000
178326731.500000
267440091.499813
401060131.499875
601490191.499917
9020135281.499945
  • 我们发现,capacity 大于 47 以后,都是呈 1.5 的倍率在增长的,不过为什么会有精度误差呢?
  • 继续输出一些信息得知:
sizecapacitycapacity / oldcapacityoldcapacity + oldcapacity / 2
015//
16312.06666722
32471.51612946
48701.48936270
711051.500000105
1061571.495238157
1582351.496815235
2363521.497872352
3535281.500000528
5297921.500000792
79311881.5000001188
118917821.5000001782
178326731.5000002673
267440091.4998134009
401060131.4998756013
601490191.4999179019
9020135281.49994513528
  • 于是我们发现,capacity 大于 47 以后,capacity = oldcapacity + oldcapacity / 2;
  • 当 oldcapacity 为偶数的时候,正好 1.5 倍;当 oldcapacity 为奇数的时候,每次除 2 在 c++ 中都是取下整的,所以才会有 1.5 倍倍增的误差;

三、扩容逻辑实现

  • 根据以上的实验得知,string 的扩容策略如下:
	size_type _Grow_to(size_type _Count) const
	{
		size_type _Capacity = capacity();
		if ( _Capacity < 32 ) {
			_Capacity = _Capacity + 16;
		}else {
			_Capacity = max_size() - _Capacity / 2 < _Capacity 
			? 0 : _Capacity + _Capacity / 2;	// try to grow by 50%
		}
		if (_Capacity < _Count)
			_Capacity = _Count;
		
		return (_Capacity);
	}
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

英雄哪里出来

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

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

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

打赏作者

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

抵扣说明:

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

余额充值