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

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虚拟环境入门与实战
本站内容用于信息整理与展示,如有侵权或内容问题请及时联系处理。

相关推荐

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

同类最新

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

更多
Linux环境下Node.js单元测试方法详解
编程语言 · 2026-06-11

Linux环境下Node.js单元测试方法详解

在Linux环境下对Node js项目进行单元测试,主流框架有Mocha、Jest和Jasmine。以Mocha为例,需先安装Node js与npm,创建package json,安装Mocha为开发依赖,建立test文件夹,编写测试用例,使用describe定义测试套件、it定义测试用例、assert断言。最后在scripts中添加test命令,通过npm

如何在Linux上全面管理Node.js依赖的实用步骤与技巧
编程语言 · 2026-06-11

如何在Linux上全面管理Node.js依赖的实用步骤与技巧

在Linux系统上,Node js依赖管理通过npm或Yarn进行,利用package json记录依赖,配合锁定文件确保版本一致。操作包括安装工具、初始化项目、安装生产与开发依赖、更新删除依赖、提交锁定文件、最小化依赖、安全审计及使用nvm管理Node js版本。

深入剖析Linux环境下ThinkPHP框架的安全风险及应对策略
编程语言 · 2026-06-11

深入剖析Linux环境下ThinkPHP框架的安全风险及应对策略

Linux环境下ThinkPHP安全取决于版本、配置与开发习惯。旧版存在preg_replace漏洞、控制器过滤不严及SQL注入风险;配置疏漏如开启调试模式、未强制路由等削弱防护。升级至6 x、关闭调试、禁用危险函数、开启强制路由、使用ORM、限制文件上传、配置防火墙与HTTPS可有效提升安全性。框架、系统、开发三位一体方能构建可靠防护。

Linux下JavaScript性能优化高效实现
编程语言 · 2026-06-11

Linux下JavaScript性能优化高效实现

在Linux环境下,JavaScript性能优化需从运行时环境、代码写法、并发处理、缓存策略、数据库优化、网络优化、监控分析、安全部署及代码分割等多环节进行迭代改进,持续精准解决性能瓶颈。

全面详解Node.js在Linux系统中的安全性保障与最佳实践
编程语言 · 2026-06-11

全面详解Node.js在Linux系统中的安全性保障与最佳实践

在Linux环境部署Node js应用,需从系统内核加固、服务精简、依赖审计、HTTPS加密、输入验证、权限分离、敏感信息管理及监控应急响应等多个环节进行系统安全防护,构建纵深防御体系,保障应用安全运行,确保系统稳健可靠。