源码版本:gcc-15.1.0
通过分析GCC的STL源码,我们可以清晰地看到 std::vector::push_back 的实现逻辑。核心代码片段如下,图中用红色框标注了关键部分:

从上图可以看到,push_back 函数首先检查当前容量是否足够。如果 _M_finish(指向最后一个元素之后)不等于 _M_end_of_storage(指向分配的存储空间末尾),说明还有预分配的空间,则直接在尾部构造新元素。
如果容量不足,则会调用 _M_reallocate_append 函数进行扩容。 对于C++11及更高版本,右值引用的 push_back 实现本质上是转发给了 emplace_back 函数。
容量不足时的代码执行过程
当需要扩容时,程序会执行复杂的内存重新分配过程。下图详细展示了这一过程的代码逻辑,并用红色箭头和文字进行了分步标注:

这个过程可以概括为以下几个关键步骤:
- 计算需要的容量:通过
_M_check_len 函数确定新的容量大小。
- 创建新的连续内存:使用分配器分配一块更大的连续内存。
- 初始化/迁移数据:将旧内存中的元素移动或拷贝到新内存。这里使用了
std::uninitialized_move_if_noexcept,它保证了异常安全——如果元素的移动构造函数可能抛出异常,则会改用拷贝构造函数。
- 释放旧内存:将旧内存块交还给分配器释放。
- 更新内部指针:将
vector 内部的 _M_start, _M_finish, _M_end_of_storage 指针指向新的内存区域。
补充说明:异常安全与优化
代码中出现的 _Guard 相关结构是保证异常安全的关键。如果在构造新元素或迁移旧元素的过程中抛出异常,这个RAII(资源获取即初始化)守卫会自动清理已经分配或部分初始化的资源,从而避免内存泄漏。
此外,_S_relocate 是 C++20 引入的优化,在硬件和编译器支持的情况下,它会使用更高效的内存重定位操作来代替逐个元素的移动或拷贝,从而提升性能。对这类底层内存操作的优化感兴趣的朋友,可以深入探索计算机基础相关的知识。
扩容算法(计算需要扩充的容量大小)
扩容的核心策略由 _M_check_len 函数决定。其源码和策略注释如下图所示:

扩容策略公式:新容量 = 当前容量 (size) + max(当前容量, 新增所需容量 (__n))
这个策略可以理解为“按需与翻倍相结合”:
- 当一次性新增的元素数量(
__n)小于当前容量时,新容量约为当前容量的2倍(翻倍扩容)。
- 当一次性新增的元素数量远大于当前容量时,新容量约为
当前容量 + __n(按需扩容),以避免一次性分配过大内存。
为了更直观地理解这个算法,可以参考下面的场景分析表格:

通过剖析 std::vector::push_back 的源码,我们不仅理解了其内存增长机制,也看到了现代C++库在实现中如何兼顾效率与异常安全。这种源码分析是深入理解编程语言和库设计思想的绝佳途径。如果你想了解更多关于C/C++底层机制的讨论,欢迎到云栈社区与更多开发者交流。
|