vector 是动态数组,在运行时,能够快速高效地添加元素。所以,在定义 vector 的时候,设定其大小是没有必要的,而且实际上会让性能更差。但是呢,只有一种特例。如果要 push_back() 的元素,所有的元素的值都是相同的,可以提前预留好空间。一旦元素的值不同,更有效的办法是定义一个空的 vector 对象,再在运行的时候,添加具体值。此外,vector 还提供了额外的方法,进一步提升动态添加元素的性能。
1、vector 对象时如何增长的。
为了支持快速随机访问,vector 将元素连续存储——每个元素紧挨着一个元素存储。假设元素是连续存储的,并且容器的大小是可变的,如果此时向 vector 或者 string 中添加新的元素,容器不可能简单地将它添加到内存的其它位置,因为元素必须是连续存储的。容器必须分配新的空间,来保存已有元素和新的元素,将已有的元素从旧位置移动到新空间。然后添加新元素,释放旧的存储空间。如果每添加一个元素,容器就执行一次内存分配和释放,性能会变得超级慢。
为了避免这种代价,标准库实现者采用了可以减少容器空间重新分配的策略。当不得不获取新的空间的时候,vector 和 string 的实现,通常会分配比需求空间更大的内存空间。这种分配策略,比每次添加新元素后都重新分配容器内存空间的策略要高效的多。即使 vector 在每次重新分配内存时,都要移动所有的元素,但是性能也比 list 和 deque 要好的多。
2、vector 可以选择自己的内存分配策略
虽然 vector 有自己的扩容机制,但是,我们仍然可以选择内存分配策略。但是必须记住:只有在迫不得已的时候,才可以重新分配内存空间。此外,还要遵循一个原则:在一个初始化为空的 vector 上调用 n 次 push_back() 来创建一个 n 个元素的 vector,花费的时间不能超过 n 的整数倍。
3、预先保留空间——reserve() 和 resize() 函数。
reserve()是容器预留空间,但并不真正创建元素对象,在创建对象之前,不能引用容器内的元素,因此当加入新的元素时,需要用push_back()/insert()函数。
resize是改变容器的大小,并且创建对象,因此,调用这个函数之后,就可以引用容器内的对象了,因此当加入新的元素时,用operator[]操作符,或者用迭代器来引用元素对象。
再者,两个函数的形式是有区别的,reserve函数之后一个参数,即需要预留的容器的空间;resize函数可以有两个参数,第一个参数是容器新的大小,第二个参数是要加入容器中的新元素,如果这个参数被省略,那么就调用元素对象的默认构造函数。下面是这两个函数使用例子:
vector<int> myVec;
myVec.reserve( 100 ); // 新元素还没有构造,
// 此时不能用[]访问元素
for (int i = 0; i < 100; i++ )
...{
myVec.push_back( i ); //新元素这时才构造
}
myVec.resize( 102 ); // 用元素的默认构造函数构造了两个新的元素
myVec[100] = 1; //直接操作新元素
myVec[101] = 2;
4、用不完的内存可以请求归还
如下代码:
int main()
{
vector<int> v1;
for (int i = 1; i < 11; i++)
{
v1.push_back(i);
}
cout << "capacity = " << v1.capacity() << " " << "size = " << v1.size() << endl;
v1.shrink_to_fit();
cout << v1.capacity() << endl;
}
输出结果:
从结果中可以看出,当 内存用不完的时候,可以请求系统归还内存,最后,使得 capacity()==size()