vector扩容时会重新分配内存、拷贝/移动旧元素、销毁旧内存,触发深拷贝且旧内存立即失效;增长倍数通常为1.5×或2×,但关键在于每次扩容都带来开销。

vector 扩容时到底发生了什么
每次 push_back 超出当前容量,vector 就得重新分配内存、拷贝旧元素、销毁旧内存——这三步全是开销。底层通常按 1.5× 或 2× 增长(具体看编译器实现,比如 libstdc++ 用 1.5,MSVC 用 2),但增长倍数不重要,关键在于:**每次扩容都触发深拷贝,且旧内存立即失效**。
常见错误现象:valgrind 报告大量无效读写、性能分析发现 operator new 占比异常高、对象析构次数远超构造次数(说明中间被反复拷贝又销毁)。
- 扩容不是“加几个字节”,而是
new一块新连续内存,再逐个调用元素的拷贝/移动构造函数 - 若元素类型没有移动构造函数(比如含
std::mutex的类),只能拷贝,开销更大 - 即使用了
emplace_back,也无法绕过内存重分配本身
reserve 什么时候该用、怎么用才有效
reserve 只影响容量(capacity),不影响大小(size),它不构造任何元素,只是提前申请好内存。但它只有在「能预估最终元素数量」时才真正省事。
使用场景:读配置文件前知道要解析多少条记录、网络收包前已知批量大小、循环前已知迭代次数。
立即学习“C++免费学习笔记(深入)”;
- 别在循环里反复调用
reserve(比如每次push_back前都reserve(size()+1)),这等于自己模拟低效扩容 - 如果预估偏差大(比如 reserve(1000) 但最后只存 3 个),浪费内存但不伤正确性;反之 reserve(10) 却 push_back 1000 次,该扩容还是得扩
- 对空
vector,reserve(N)后capacity()至少为N,但size()仍为 0
示例:
std::vector<std::string> v;<br>v.reserve(1000); // 立即分配足够存 1000 个 string 的内存<br>// 后续 1000 次 push_back 不会触发扩容
reserve 和 resize 的根本区别
reserve 是管“地”,resize 是管“人”——前者只申请内存空间,后者会实际构造/销毁元素。
错误用法:用 resize 替代 reserve 来“预分配”,结果所有元素都被默认构造(比如 std::string 构造了 1000 个空串,后续还要赋值覆盖)。
-
v.reserve(N):容量 ≥ N,大小不变,无构造/析构发生 -
v.resize(N):大小变成 N,若原 size N,则末尾元素被析构 - 对 POD 类型(如
int),resize不初始化,但reserve同样不碰数据——别指望它们清零
move 语义和 reserve 的配合效果
即使用了 reserve,如果后续 push_back 的是左值,仍可能触发拷贝;换成右值或启用移动构造,才能把扩容前的拷贝开销压到最低。
性能影响明显:对于大对象(如含大 buffer 的自定义类),移动比拷贝快一个数量级;但前提是类自己实现了移动构造函数。
- 确认你的类型支持移动:检查是否有
T(T&&)且未被 =delete - 传入
push_back(std::move(x))或直接emplace_back(...)避免临时对象拷贝 - 注意:
reserve不能让移动变快,但它确保移动只发生一次(扩容时),而不是每次扩容都移动一遍旧数据
容易被忽略的一点:vector 的迭代器在 reserve 后依然有效,但只要发生任何导致扩容的插入(哪怕只是一次 push_back),所有迭代器、指针、引用全部失效——这点和 reserve 是否调用过无关。









