游乐游手机版
首页/编程语言/文章详情

C++中push_back与emplace_back的区别

时间:2026-06-11 06:57
push_back要求传入已构造对象,内部再拷贝或移动构造;emplace_back接收构造函数参数,通过完美转发在容器内存上就地构造,省去临时对象创建和拷贝移动开销,因此性能更为优越。应优先使用emplace_back以减少不必要的复制。

许多 C++ 开发者一直对 push_back()emplace_back() 的区别感到困惑:它们究竟有何不同?这次我直接查阅了 libstdc++ 的源代码,终于彻底理解了底层实现的本质差异。下面把我的核心发现分享出来,帮助大家在实际开发中做出更高效的选择。

C++中的push_back与emplace_back的区别?

  • allocate 源码:gcc.gnu.org/onlinedocs/…
  • push_back 实现:gcc.gnu.org/onlinedocs/…
  • emplace_back 实现:gcc.gnu.org/onlinedocs/…

先来看 push_back() 的工作流程。它要求传入一个已经构造好的对象(可以是左值或右值),然后在容器内部通过拷贝构造或移动构造来创建副本。换句话说,你传入一个临时对象,容器内部还需要再“复制”或“移动”一次,这就会多产生一次对象构造的开销。

// GCC libstdc++ vector
void push_back(const value_type& __x) {
    if (this->_M_impl._M_finish != this->_M_impl._M_end_of_storage) {
        // 空间足够,在已开辟的内存上【拷贝构造】新对象
        this->_M_impl.construct(this->_M_impl._M_finish, __x);
        ++this->_M_impl._M_finish;
    } else {
        // 空间不足,扩容并移动/拷贝旧数据
        _M_realloc_insert(end(), __x);
    }
}void push_back(value_type&& __x) {
    if (this->_M_impl._M_finish != this->_M_impl._M_end_of_storage) {
        // 空间足够,在已开辟的内存上【移动构造】新对象
        this->_M_impl.construct(this->_M_impl, this->_M_impl._M_finish, std::move(__x));
        ++this->_M_impl._M_finish;
    } else {
        _M_realloc_insert(end(), std::move(__x));
    }
}void 
construct(pointer __p, const _Tp& __val)  
{ ::new((void *)__p) _Tp(__val); }       /* 直接进行拷贝/移动构造 */

emplace_back() 走的则是完全不同的路径——它接收的不是已经构造好的对象,而是元素类型构造函数的参数。通过可变模板参数和完美转发机制,它直接在容器预留的内存上调用元素的构造函数,从而省去了创建临时对象再拷贝或移动的中间环节,在性能上更优。

template<typename... _Args>
void
emplace_back(_Args&&... __args) {
    if (this->_M_impl._M_finish != this->_M_impl._M_end_of_storage) {
        // 空间足够,直接在目标内存上【就地构造】
        this->_M_impl.construct(this->_M_impl, this->_M_impl._M_finish,
                                 std::forward<_Args>(__args)...);
        ++this->_M_impl._M_finish;
    } else {
        _M_realloc_insert(end(), std::forward<_Args>(__args)...);
    }
}template<typename... _Args>              /* 可变模版参数 */ 
void
construct(pointer __p, _Args&&... __args)
{ ::new((void *)__p) _Tp(std::forward<_Args>(__args)...); } /* 完美转发 */

简单总结一下两者的核心区别:

  • 接口调用方式不同push_back() 接收的是已经构造好的对象(左值或右值引用),而 emplace_back() 接收的是元素类型的构造参数(理解起来可能有点绕,但掌握后非常直观)。
  • 底层实现机制不同push_back() 需要先在外层创建临时对象,再通过拷贝或移动构造将其“搬入”容器;emplace_back() 则直接在容器预留的内存上通过完美转发调用构造函数,完全省去了临时对象的创建与拷贝/移动步骤。对于复杂对象(如包含动态申请内存、资源管理的类型),这个性能差异非常明显,在 C++ 开发中值得优先考虑。
来源:https://juejin.cn/post/7648213611392434195
上一篇Qt坐标体系入门:从GUI概念到坐标实战 下一篇Python venv虚拟环境入门与实战
本站内容用于信息整理与展示,如有侵权或内容问题请及时联系处理。

相关推荐

补充同频道和同主题内容,方便继续浏览更多相关内容。

同类最新

继续查看同栏目最近更新的文章。

更多
IDEA中SpringBoot项目热部署实现指南
编程语言 · 2026-07-11

IDEA中SpringBoot项目热部署实现指南

Springboot项目在IDEA中实现热部署需依次完成:开启自动编译(静态与动态编译)、启用热部署策略、引入spring-boot-devtools依赖、关闭浏览器缓存,最后启动测试即可生效,省去手动重启时间,大幅提升开发效率。

Java实现Excel转JSON的代码详解
编程语言 · 2026-07-11

Java实现Excel转JSON的代码详解

使用Java结合FreeSpire XLS库可自动将Excel转为JSON,通过读取工作表并映射为键值对,高效支持数据迁移、报表导出与系统对接,大幅提升效率并消除手动录入错误。

Golang高效布谷鸟过滤器多字符集字符串过滤
编程语言 · 2026-07-11

Golang高效布谷鸟过滤器多字符集字符串过滤

布谷鸟过滤器支持删除操作,通过指纹截断与双哈希定位实现多字符集高效过滤。指纹截断需采用fnv64a或xxhash快速哈希并取低8位,桶索引使用独立双哈希避免假阳性。并发安全需以固定数组配合sync atomic实现。

Java使用Poi-tl按Word模板生成动态表格
编程语言 · 2026-07-11

Java使用Poi-tl按Word模板生成动态表格

Poi-tl是Word导出工具,文档周全,支持多级合并表头生成。通过{{text}}、{{?var}}、{{ table}}标签处理模板,传入TableRenderData对象即可动态渲染表格。示例展示用户信息表格生成,并提供工具类消除表格首行缩进,确保格式正确。

Go语言微服务集成Swagger生成交互式API文档完整教程
编程语言 · 2026-07-11

Go语言微服务集成Swagger生成交互式API文档完整教程

在Go微服务中集成Swagger常见四大问题:swaginit初始化失败需确保存在packagemain及注释;SwaggerUI加载失败因路由映射错误或未导入docs;@Param注解必须严格遵循格式;部署后接口文档显示localhost因硬编码host,应删除或通过环境变量动态注入。