C++八股文:vector扩容机制选择

一句话时间

“vector 在容量用尽时会分配更大连续区并把旧元素搬到新区(移动或拷贝),这是摊销常数时间保证的来源;增长因子实现定义,常用 2× 或 1.5×,扩容会使所有指针/引用/迭代器失效,使用 reserve 和 emplace_back 可避免或减小开销。


关键细节(逐条要点)

  • 触发条件:当调用 push_back/emplace_back/insert并且size() == capacity() 时触发重新分配。reserve(n)` 可以提前设置容量,避免扩容。
  • 增长策略是实现定义的:标准不规定确切增长因子;常见实现:libstdc++ 常见以接近 2×(倍增)增长,libc++ / MSVC 常见以约 1.5× 的增长(实现细节随版本变化)。
  • 搬迁(move vs copy):重新分配时元素会被 搬到 新内存:优先使用 移动构造(move),如果没有 noexcept 的移动构造,会退回到拷贝构造(库通常使用 std::move_if_noexcept 策略以保证异常安全)。
  • 异常安全:为了避免在搬迁中抛出导致状态损坏,标准库倾向于在可能抛出的移动构造无法保证安全时使用拷贝;如果元素既不可拷贝又移动会抛,扩容会导致异常传播(操作失败),但实现会保证容器不会泄露资源且处于合法状态(具体保证级别依实现)。
  • 代价:push_back 是 摊销常数时间(amortized O(1)),但扩容一次需要 O(n) 来搬移所有元素(n=旧 size)。
  • 后果:扩容 使所有指针/引用/迭代器失效(到元素),除了某些实现对 std::vector<bool> 等特化以外。
  • 初始容量:默认空 vector 的 capacity() 常为 0;第一次扩容常变为 1 或实现选择的最小容量。
  • shrink_to_fit():只是“请求”释放多余容量,非强制(实现可忽略);swap 一个空的临时向量是更强的收缩方式。
  • resize(n):改变 size(会构造/析构元素),如果 n 大于 capacity 会触发扩容。reserve(n) 只改变 capacity 不改变 size。

面试常问要点(简洁可背)

  • 什么时候扩容? 当 size()==capacity 再插入元素。可用 reserve() 预分配避免扩容。
  • 复杂度? 平均/摊销 O(1),单次扩容 O(n)。
  • 元素如何搬移? 使用移动构造(若可用且 noexcept),否则拷贝构造(move_if_noexcept)。
  • 扩容对迭代器/指针影响? 全部失效(指向元素的指针/引用/迭代器都可能失效)。
  • 如何避免扩容开销? 事先 reserve(预计大小);尽量用 emplace_back 就地构造。
  • 增长因子是多少? 实现定义,常见为 2× 或 ~1.5×(不要死记,面试说“实现定义,常见2×或1.5×”即可)。
  • 异常安全怎么做的? 库用 move_if_noexcept 策略,优先选择不会抛的操作以提供更强的保证;若无法,扩容可能抛出。

更多推荐