在C++标准库中,vector是最常用的序列式容器之一,它的底层基于连续内存空间实现,支持动态扩容,这也是它和普通数组最大的区别。vector的扩容机制直接影响了容器的内存使用效率和操作性能,是开发者必须掌握的核心知识点。

vector容量相关核心概念
要理解vector的扩容机制,首先需要明确两个核心属性的含义:
- size:表示当前vector中实际存储的元素个数,调用
size()方法可以获取该值。 - capacity:表示当前vector底层分配的内存空间最多可以存储的元素个数,调用
capacity()方法可以获取该值。
当vector的size等于capacity时,如果再添加新的元素,vector就会触发扩容操作。
vector扩容的基本流程
vector扩容不是简单的在原内存空间后追加内存,而是遵循以下完整流程:
- 申请一块新的、更大的连续内存空间,新的容量通常是原容量的1.5倍或者2倍,具体倍数由编译器和标准库实现决定。
- 把原内存空间中的所有元素拷贝或者移动到新的内存空间中。
- 释放原内存空间。
- 在新内存空间的末尾添加新元素,更新size和capacity的值。
注意:扩容过程中元素会发生迁移,因此之前指向原vector元素的指针、引用、迭代器都会失效,开发时需要特别注意这一点。
不同标准下的扩容倍数差异
C++标准并没有规定vector扩容的具体倍数,只要求扩容后的容量必须大于等于原来的容量,因此不同编译器的标准库实现会有差异:
| 编译器/标准库 | 常见扩容倍数 |
|---|---|
| GCC的libstdc++ | 2倍 |
| Clang的libc++ | 2倍 |
| MSVC的STL | 1.5倍 |
代码示例验证扩容过程
下面通过一段C++代码来直观展示vector的扩容现象:
#include <iostream>
#include <vector>
int main() {
std::vector<int> vec;
// 初始状态下size和capacity都是0
std::cout << "初始状态: size=" << vec.size() << ", capacity=" << vec.capacity() << std::endl;
// 循环添加元素,观察扩容触发时机
for (int i = 0; i < 10; ++i) {
vec.push_back(i);
std::cout << "添加元素" << i << "后: size=" << vec.size() << ", capacity=" << vec.capacity() << std::endl;
}
return 0;
}
在GCC环境下运行上述代码,输出结果通常如下:
初始状态: size=0, capacity=0 添加元素0后: size=1, capacity=1 添加元素1后: size=2, capacity=2 添加元素2后: size=3, capacity=4 添加元素3后: size=4, capacity=4 添加元素4后: size=5, capacity=8 添加元素5后: size=6, capacity=8 添加元素6后: size=7, capacity=8 添加元素7后: size=8, capacity=8 添加元素8后: size=9, capacity=16 添加元素9后: size=10, capacity=16
可以看到每次触发扩容时,新的capacity都是原capacity的2倍,和之前提到的GCC标准库实现规则一致。
如何优化vector扩容开销
频繁扩容会带来内存申请和数据拷贝的开销,如果开发者提前知道vector需要存储的大致元素个数,可以通过reserve()方法提前分配足够的容量,避免多次扩容:
#include <iostream>
#include <vector>
int main() {
std::vector<int> vec;
// 提前预留10个元素的容量
vec.reserve(10);
std::cout << "reserve后: size=" << vec.size() << ", capacity=" << vec.capacity() << std::endl;
// 添加10个元素不会触发扩容
for (int i = 0; i < 10; ++i) {
vec.push_back(i);
}
std::cout << "添加10个元素后: size=" << vec.size() << ", capacity=" << vec.capacity() << std::endl;
return 0;
}
需要注意的是,reserve()只改变capacity,不会改变size,调用后不能直接通过下标访问元素,否则会出现越界访问的错误。
size和capacity的手动调整
除了reserve()可以调整capacity之外,resize()方法可以同时调整size和capacity:
- 如果新的size小于原size,会截断多余的元素,capacity不会减小。
- 如果新的size大于原size但小于原capacity,会新增元素(默认初始化),capacity不变。
- 如果新的size大于原capacity,会触发扩容,扩容后的容量至少等于新的size。
另外,C++11之后提供了shrink_to_fit()方法,可以请求vector将capacity减小到和size相等,不过这个方法只是请求,标准库并不保证一定会执行,具体是否生效取决于实现。