C++ STL string 扩容策略

一、引例

1、string 扩容概述

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

2、扩容尝试

  • 通过 VS2013 环境下,string 在一个一个插入元素 (push_back)的过程中,size 和 capacity 的对应关系如下;
size capacity
0 15
1 15
15
14 15
15 15
16 31
17 31
31
30 31
31 31
32 47
33 47
  • 观察发现,只要 string 这个对象创建出来了,无论有没有元素,就占用了15个字节,当 size 达到 capacity 时,又增加了 16 个字节;

二、扩容逻辑猜测

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

1、猜测一:常数增量

  • 目前观察得到的 capacity 的序列为:
	15 -> 31 -> 47
  • 猜测 string 的增长策略为每次 capacity 不足时,采取增加 16 个字节的方案;
  • 因为数据太少没有说服力,所以我们准备更多数据,再来看看情况如何:
size capacity capacity - oldcapacity
0 15 0
16 31 16
32 47 16
48 70 23
71 105 35
106 157 52
158 235 78
236 352 117
353 528 176
529 792 264
793 1188 396
1189 1782 594
1783 2673 891
2674 4009 1336
4010 6013 2004
6014 9019 3006
9020 13528 4509
  • 当某次 size > capacity 时,增长的 capacity 增量并不是一个常数,所以 猜测一 被推翻;

2、猜测二:倍数增量

  • 猜测是倍增的策略以后,我们就要看这个倍增因子了,将每次 size > capacity 时,两次 capacity 相除,得到如下的表:
size capacity capacity / oldcapacity
0 15 /
16 31 2.066667
32 47 1.516129
48 70 1.489362
71 105 1.500000
106 157 1.495238
158 235 1.496815
236 352 1.497872
353 528 1.500000
529 792 1.500000
793 1188 1.500000
1189 1782 1.500000
1783 2673 1.500000
2674 4009 1.499813
4010 6013 1.499875
6014 9019 1.499917
9020 13528 1.499945
  • 我们发现,capacity 大于 47 以后,都是呈 1.5 的倍率在增长的,不过为什么会有精度误差呢?
  • 继续输出一些信息得知:
size capacity capacity / oldcapacity oldcapacity + oldcapacity / 2
0 15 / /
16 31 2.066667 22
32 47 1.516129 46
48 70 1.489362 70
71 105 1.500000 105
106 157 1.495238 157
158 235 1.496815 235
236 352 1.497872 352
353 528 1.500000 528
529 792 1.500000 792
793 1188 1.500000 1188
1189 1782 1.500000 1782
1783 2673 1.500000 2673
2674 4009 1.499813 4009
4010 6013 1.499875 6013
6014 9019 1.499917 9019
9020 13528 1.499945 13528
  • 于是我们发现,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);
	}

猜你喜欢

转载自blog.csdn.net/WhereIsHeroFrom/article/details/108817346