条目十四《使用reserve来避免不必要的重新分配》
使用vector和string的插入元素的时候,我们是不用担心内存问题的(只要不超过容器的max_size)。因为底层有分配子管理内存。在插入元素的时候,内存不够会发生像realloc的过程:
- 分配新的内存块,它有容器目前容量的几倍。在大部分实现中,vector和string的容量每次以2为因数增
长。也就是说,当容器必须扩展时,它们的容量每次翻倍。- 把所有元素从容器的旧内存拷贝到它的新内存。
- 销毁旧内存中的对象。
- 回收旧内存
先看个例子
vector<int> vec;
for(int i = 0; i < 10; i++)
{
vec.push_back(i)
}
这个过程会发生最少4次重新分配内存的过程。再来看下一个重新分配内存涉及到哪些过程:
- 1.new操作-->malloc
- 2.拷贝操作
- 3.析构对象
- 4.释放内存
咋一看,一两次还可以接受,当插入的元素越来越多的时候,对性能的消耗是非常客观的。
上面的问题还只是其中一个问题,在插入元素时,重新分配内存会造成迭代器、指针和引用的失效。
为了优化这两个问题,在使用vector和string的时候,可以使用reserve函数。先引出四个关于容器大小和设置的成员函数:
-
size()———————获得容器的元素个数
-
capacity()———获得容器的容量
-
resize()—————强制设置容器的元素个数(参数大于当前大小,调用默认构造函数创建元素填充在尾部。小于当前大小,会析构并销毁多余的元素)
-
reserve()————强制设置容器的容量大小(参数小于现有的容量大小会忽略当前调用,大于会扩容。)
为了避免容器的不必要扩容而造成的消耗,在初始化容器的时候可以通过reserve()设置容器的容量大小,这样在插入元素的是够,只要当前的元素个数小于容量大小,都不会发生重新分配内存。这样也就不会发生迭代器、指针和引用失效等问题,也就没有多次拷贝,析构对象,释放内存的现象发生。
vector<int> vec;
vec.reserve(15);
for(int i = 0; i < 10; i++)
{
vec.push_back(i)
}
这个过程在reserve后插入元素不会发生重新分配内存过程。因为,插入的元素个数(10)小于容量的大小(15)。
可能有人说,在开始时设置过多的容量,那么不就相当于数组吗?浪费了剩余没用到内存,不用担心这个问题,我们可以等插入元素完毕的时候:
- 调用sesize()来修剪大小
- 使用swap()技巧。(vector
tmp_vec(vec).swap(vec) 利用容器的拷贝构造不会复制空内存的原理,先创建一个纯净的临时容器,然后再交换容器内容,原来的容器就可以成为一个没有多余容量的容器,节省内存哦)