c++oding="utf-8" ?>
std::vector扩容在size()等于capacity()时触发,需重新分配内存并搬移数据,导致迭代器失效;主流实现采用1.5x或2x增长因子,reserve()可预分配避免意外扩容。

std::vector 的扩容不是“加一点”,而是重新分配一块更大的连续内存,再把旧数据搬过去——这是所有性能问题的起点。
扩容触发条件:size() == capacity() 时才发生
很多人误以为 push_back() 每次都分配内存。实际上,只有当前元素数量 size() 等于已分配容量 capacity() 时,下一次插入才会触发扩容。比如容量为 4、已有 4 个元素,第 5 次 push_back() 才会 realloc。
- 初始状态:空
vector的capacity()通常为 0,首次push_back()后变为 1 - 不触发扩容的操作:
pop_back()、clear()、甚至多次reserve()都不会导致数据搬迁 - 容易被忽略的触发点:
insert()在中间位置插入,也可能因空间不足而扩容(不只是尾插)
扩容倍数不是标准规定的,但主流实现是 1.5x 或 2x
C++ 标准只要求扩容后容量 > 当前 size,不规定增长因子。实际行为取决于 STL 实现:
- GCC(libstdc++):约 1.5 倍,序列常为 1 → 2 → 3 → 4 → 6 → 9 → 13…
- MSVC:倾向 2 倍,序列常为 1 → 2 → 4 → 8 → 16…
- Clang(libc++):与 GCC 接近,也采用非整数倍策略以节省内存
- 注意:
reserve(n)不受此规则限制,它直接申请至少 n 容量,不关心“倍数”
扩容代价高,且会让所有迭代器/指针/引用失效
一次扩容 = 内存分配 + 元素逐个移动(或拷贝)+ 旧内存释放。对大对象或自定义类型,这可能很慢;更关键的是副作用:
-
iterator、const_iterator、pointer、reference在扩容后全部悬空,解引用即未定义行为 - 如果你在循环中边遍历边
push_back(),很可能在某次迭代中途触发扩容,导致迭代器失效崩溃 - 移动语义能缓解拷贝开销,但无法避免内存重分配本身
- 用
reserve()预估容量,是规避意外扩容最直接有效的方式
真正难处理的不是“怎么扩容”,而是“什么时候扩容不可见但已发生”。观察 capacity() 变化比只看 size() 更能暴露潜在风险。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











