ST
L 标准只规定 push_back 在需要扩容时必须保证摊还时间复杂度为 O(1),但**不强制要求倍增**;实际主流实现(libstdc++、libc++、MSVC STL)都采用「1.5 倍或 2 倍」增长策略,具体取决于实现:
2 * old_capacity,GCC 13+ 默认改用 old_capacity + old_capacity / 2(即 1.5 倍),避免内存浪费2 * old_capacity
2 * old_capacity
这意味着你不能假设所有平台扩容后容量一定是翻倍——尤其在跨平台内存敏感场景下,capacity() 的增长路径可能不同。
固定增量会导致 push_back 摊还复杂度退化为 O(n):插入 n 个元素需约 n²/10 次拷贝。倍增则把拷贝总次数压到 O(n) 级别。
举个例子:从空开始插入 16 个元素,按 2 倍扩容(0→1→2→4→8→16):
拷贝次数 = 1 + 2 + 4 + 8 = 15
而按 +10 扩容(0→10→20):前 10 次无拷贝,第 11 次触发扩容,拷贝 10 个旧元素;后续插入不再拷贝——看似好?但插入 1000 个元素时,会触发约 100 次扩容,拷贝总量超 50000 次。
倍增的本质是用空间换均摊时间,且避免频繁小内存分配带来的堆管理开销。
reserve(n) 只改变容量(capacity()),不改变大小(size()),也不会构造对象;resize(n) 改变大小,若 n > size() 则会默认构造新增元素,并在必要时自动扩容。
reserve(100) 后,push_back 在第 101 次才可能触发下一次扩容resize(200) 时,如果当前 capacity() ,会先按实现策略分配新内存(比如扩到 256),再构造 200 个对象
resize(50)(当前 size=100)不会释放内存,capacity() 不变——这是常见误区shrink_to_fit() 是非绑定请求,标准允许其实现“忽略”。libstdc++ 和 libc++ 通常会 realloc 并复制,但 MSVC STL 在某些版本中直接不响应。
更麻烦的是:即使缩容成功,也可能因内存对齐、分配器策略(如 small buffer optimization)导致 capacity() 仍大于 size()。
安全做法是:仅在明确内存压力大、且已 profiling 确认其有效时才用;否则优先靠 reserve() 预估,避免反复扩容缩容。
真正不可绕过的细节是:**vector 的迭代器/指针在任何扩容发生时全部失效**——哪怕只是 push_back 一次,只要触发了 reallocation,所有现存 &v[i]、v.begin() 都变成悬垂指针。