vector 是标准STL序列容器,它是在一个连续内存块中保存它的元素,经常用作数组的替代品,在使用过程中,无需关注内存的动态分配,在使用时非常方便。然而,在使用vector时,若使用不当,也会存在有很多陷阱。比较常见的问题有如下几个:
(注:本文中所有测试代码编译环境为:gcc版本4.6.3。)
-
扩容问题
首先来看一个示例:
#include<vector>
#include<string>
#include<iostream>
int main()
{
std::vector<int> vint;
for (int i = 0; i < 6; ++i)
{
vint.push_back(i);
std::cout<<"capacity:"<<vint.capacity()<<std::endl;
std::cout<<"size:"<<vint.size()<<std::endl;
}
return 0;
}
输出结果为:
capacity:1
size:1
capacity:2
size:2
capacity:4
size:3
capacity:4
size:4
capacity:8
size:5
capacity:8
size:6
从示例的输出结果,可以看出,vector的初始容量为0,插入1个元素后,容量为1,插入2个元素后,容量为2,而当插入第3个元素时,容量变为了4,直到4个容量全部用完后,再次插入新元素时,容易将扩充到8,由此可见,vector的内存扩容,是发生在当前容量无法容纳新元素时进行的,并且,每次扩容后的大小是原容量的2倍。那么,扩容的内存空间,是在原内存地址上追加呢?还是重新申请一块内存呢?由于vector中的元素是存储在一个连续的内存块中,因此,我们可以把vector第一个元素的地址打印出来,查看其首地址是否发现了变化,示例如下:
int main()
{
std::vector<int> vint;
for (int i = 0; i < 6; ++i)
{
vint.push_back(i);
std::cout<<"address:"<<&vint[0]<<std::endl;
}
return 0;
}
输出结果为:
address:0x930f008
address:0x930f018
address:0x930f028
address:0x930f028
address:0x930f040
address:0x930f040
通过输出结果,我们可以发现,每当vector进行一次扩容后,其首地址都发生了变化,因此,我们基本可以确定,vector进行扩容时,并不是直接在原地址末尾追加内存,而是重新申请了一块新内存。并将原vector中的元素拷贝到新内存块中。
在这种内存扩容机制下,如果对未初始化容量的vector进行大量插入操作时,势必会引起,大量的数据“搬家”现象,从而导致vector内大量元素的构造、析构及拷贝,而这些都是代价较大地操作。
解决方案
解决该问题方法,一般是事先预估vector中的数据量,初始化vector容量,从而减少扩容的次数,示例