0. 写在最前面

本文持续更新地址:https://haoqchen.site/2020/01/17/emplace_back-vs-push_back/

std::vector中实现了这两个函数,主要作用都是向一个vector中增加一个元素,但它们其实有很多细微的差别。有很多人似乎对这两个函数有一些误解,找了一些资料,然后自己做了个实验总结了一下这两个函数的异同。

如果觉得写得还不错,可以找我其他文章来看看哦~~~可以的话帮我github点个赞呗。
你的Star是作者坚持下去的最大动力哦~~~

1. 总结及推荐

1.1 异同点

  1. 如果参数是左值,两个调用的都是copy constructor
  2. 如果参数是右值,两个调用的都是move constructor(C++ 11后push_back也支持右值)
  3. 最主要的区别是,emplace_back支持in-place construction,也就是说emplace_back(10, “test”)可以只调用一次constructor,而push_back(MyClass(10, “test”))必须多一次构造和析构

1.2 需要澄清的一些误解

  1. emplace_back的效率比push_back高,无论什么情况下都高,所以可以无脑用。从上面1、2点可以看到,两者其实没有区别
  2. push_back不支持右值参数,不能调用move constructor,效率低。由C++ Reference可以得知,C98是没有右值形参的,但C++11已经增加了。
  3. emplace_back的优势是右值时的效率优化。这是最大的误解,emplace_back的最大优势是它可以直接在vector的内存中去构建对象,不用在外面构造完了再copy或者move进去!!!

1.3 使用建议

  1. 左值用push_back
  2. 右值用emplace_back
  3. 局部变量尽量使用emplace_backin-place构建,不要先构建再拷贝或移动。

2. 实际测试

2.1 测试代码

#include <iostream>
#include <string>
#include <vector>
#include <time.h>class BaseClass
{public:BaseClass(const std::string name, int count) : name_(name), count_(count){std::cout << name_ << " constructor called" << std::endl;} BaseClass(const BaseClass& b){this->name_ = b.name_;std::cout << name_ << " copy constructor called" << std::endl;} BaseClass(BaseClass&& b){this->name_ = b.name_;std::cout << name_ << " move constructor called" << std::endl;} virtual ~BaseClass(){std::cout << name_ << " destructor called" << std::endl;}
private:std::string name_;int count_;
};int main(int argc, char** argv)
{std::vector<BaseClass> vec_bc;std::vector<std::string> vec_s;vec_bc.reserve(10); // 不提前reserve会有多次拷贝操作std::cout << "--------------------------------emplace_back rvalue:" << std::endl;vec_bc.emplace_back(BaseClass("b1", 1));vec_bc.emplace_back("b1_1", 1);std::cout << "--------------------------------push_back rvalue:" << std::endl;vec_bc.push_back(BaseClass("b3", 1));// vec_bc.push_back("b4_1");// 不能通过编译std::cout << "--------------------------------emplace_back lvalue:" << std::endl;BaseClass b2("b2", 1);vec_bc.emplace_back(b2);vec_bc.emplace_back(std::move(b2));std::cout << "--------------------------------push_back lvalue:" << std::endl;BaseClass b4("b4", 1);vec_bc.push_back(b4);vec_bc.push_back(std::move(b4));// vec_bc.shrink_to_fit(); // 存在移动的可能std::cout << "--------------------------------destruct:" << std::endl;
}

2.2 编译指令

g++ emplace_push_back.cpp -std=c++11 -o test -O0

2.3 结果输出

--------------------------------emplace_back rvalue:
b1 constructor called
b1 move constructor called
b1 destructor called
b1_1 constructor called
--------------------------------push_back rvalue:
b3 constructor called
b3 move constructor called
b3 destructor called
--------------------------------emplace_back lvalue:
b2 constructor called
b2 copy constructor called
b2 move constructor called
--------------------------------push_back lvalue:
b4 constructor called
b4 copy constructor called
b4 move constructor called
--------------------------------destruct:
b4 destructor called
b2 destructor called
b1 destructor called
b1_1 destructor called
b3 destructor called
b2 destructor called
b2 destructor called
b4 destructor called
b4 destructor called

2.4 结果分析

  1. 可以看到,emplace和push对于右值是没有差异的,左值也是没有差异的,调用的构造函数类型和数量都是一样的。
  2. 区别在于b1_1这种构造方式,emplace_back只调用一次普通构造函数,push_back直接不支持这种操作。

2.5 我该怎么办

先来看两者的实现源码:

void push_back(const value_type& __x)
{if (this->_M_impl._M_finish != this->_M_impl._M_end_of_storage){_Alloc_traits::construct(this->_M_impl, this->_M_impl._M_finish, __x);++this->_M_impl._M_finish;}else#if __cplusplus >= 201103L_M_emplace_back_aux(__x);#else_M_insert_aux(end(), __x);#endif
}void push_back(value_type&& __x)
{ emplace_back(std::move(__x));
}#if __cplusplus >= 201103L
template<typename _Tp, typename _Alloc>
template<typename... _Args>
void vector<_Tp, _Alloc>::emplace_back(_Args&&... __args)
{if (this->_M_impl._M_finish != this->_M_impl._M_end_of_storage){_Alloc_traits::construct(this->_M_impl, this->_M_impl._M_finish, std::forward<_Args>(__args)...);++this->_M_impl._M_finish;}else_M_emplace_back_aux(std::forward<_Args>(__args)...);
}
#endif

可以看到:

  1. 当参数是左值时,push_backemplace_back的区别在于后者对参数多进行了一个std::forward操作
  2. 当参数是右值时,push_back其实是调用的emplace_back实现的。

所以什么时候该用什么就很明显了。

3. 题外话

上面的代码有两句话特别注释了,分别是:

vec_bc.reserve(10); // 不提前reserve会有多次拷贝操作

以及

// vec_bc.shrink_to_fit(); // 存在移动的可能

大家可以注释掉第一句或者取消第二句的注释试试,会出现非常多莫名其妙的copy constructor和析构。

push_backemplace_back的说明里都有类似的一句话:“This effectively increases the container size by one, which causes an automatic reallocation of the allocated storage space if -and only if- the new vector size surpasses the current vector capacity.”

意思就是vector的size超过capacity时会重新进行内存分配(一般是double),并把原有的数据拷贝到新申请的地址。这是vector的实现细节,有兴趣可以自己钻研。

参考

  • C++ Reference std::vector::push_back
  • C++ Reference std::vector::emplace_back

喜欢我的文章的话Star一下呗Star

版权声明:本文为白夜行的狼原创文章,未经允许不得以任何形式转载

C++性能之战(3)--emplace_back VS push_back相关推荐

  1. C++11:右值引用、移动构造、std::move, 以及使用emplace_back代替push_back

    最近在写一段代码的时候,突然很好奇C++11中对push_back有没有什么改进以增加效率,上网搜了一些资料,发现果然新增了emplace_back方法,比push_back的效率要高很多. 1.右值 ...

  2. c++11中emplace_back vs push_back

    引言 在C11中,有两种方法可以把元素放入容器中:emplace_back和push_back. push_back是C11之前就有的,而emplace_back是C11中新加的. 既然它们的作用都是 ...

  3. C++11:右值引用、move, 以及使用emplace_back代替push_back

    最近在写一段代码的时候,突然很好奇C++11中对push_back有没有什么改进以增加效率,上网搜了一些资料,发现果然新增了emplace_back方法,比push_back的效率要高很多. 1.右值 ...

  4. C++11使用emplace_back代替push_back

    最近在写一段代码的时候,突然很好奇C++11中对push_back有没有什么改进以增加效率,上网搜了一些资料,发现果然新增了emplace_back方法,比push_back的效率要高很多. 首先,写 ...

  5. emplace_back与push_back异同

    vector的emplace_back与push_back 文章目录 vector的emplace_back与push_back 前言 1.区别总览 2.push_back 支持右值引用 不支持传入多 ...

  6. emplace_back和push_back对比分析

    emplace_back含义 emplace_back是C++11新引进的接口函数. emplace_back是就地构造,不用构造后再次复制到容器中.因此效率更高. push_back 简单的一个案例 ...

  7. emplace_back和push_back的区别

    相同点:两者都是向容器内添加数据 不同点:当数据为类的对象时,emplace_back相对push_back可以避免额外的移动和复制操作. 以下代码copy from点击打开链接 #include & ...

  8. C++ 中emplace_back和push_back差异

    前言 最近看rocskdb源码,发现了大量的设计模式和C++高级特性,特此补充一下,巩固基础. 问题描述 其中关于动态数组的元素添加,代码中基本将push_back抛弃掉了,全部替换为emplace_ ...

  9. emplace_back与push_back的区别

    下面举了几种比较常见的情况:(以下所有代码均假设存在一个Teacher类) 1. #include <vector>int main(){ std::vector<Teacher&g ...

最新文章

  1. Ethereum 君士坦丁堡安全漏洞对 FOD 的影响
  2. RHEL5.3下搭建LAMP+Django环境(二)
  3. 一个mp4文件分析工具
  4. 【TypeScript系列教程04】编译参数
  5. 疯狂工作流讲义(第2版)基于Activiti6.x电子书
  6. lightning接口_苹果、安卓充电接口有望统一,欧盟重压下,苹果将不得不妥协
  7. 【云计算学习教程】什么是中间件?常见中间件有哪些?
  8. 哥斯拉Godzilla shell管理工具
  9. oppo鸿蒙系统刷机包下载,oppo A11N原版系统rom固件刷机包下载20200716版卡刷包
  10. Arduino开发遥控小车(二)基于nRF24L01无线模块实现数据发送和接收
  11. 怎样自制微信gif动态表情包?
  12. 基础软件照搬开源不可取,自力更生才是正途
  13. python游戏辅助lol_GitHub - skyedai910/lol-skin-spider: 30行Python代码爬取英雄联盟全英雄全皮肤...
  14. Clonezilla制作镜像、恢复
  15. react如何请求amr文件流接口-优化版
  16. Android mc怎么和win10联机,我的世界实现跨平台联机 Win10玩家可与手机互联
  17. mybatisplus 自增主键失效,自增主键超大
  18. 设置计算机关机时间快捷键,怎么让电脑定时关机设置大全
  19. DevOps进阶(十三)初识JFrog Artifactory
  20. 网页宽度打印出A4纸

热门文章

  1. css 背景效果_软件技术:我写CSS的常用套路(附demo的效果实现与源码)
  2. NFT,不止于投机!
  3. 8乘8led点阵显示数字_基于51单片机的8乘8LED点阵显示屏的设计
  4. 51 单片机 点阵 LED 显示屏程序
  5. Python等编程语言学习资料分享
  6. matlab 星座图 qam,16QAM_星形及矩形星座图调制解调MATLAB代码.doc
  7. Linux--date命令
  8. spring security 3配置ACL时报java.lang.NoSuchMethodError: net.sf.ehcache.Cache.init
  9. 安卓报错:E/EGL_adreno: tid 2148: eglSurfaceAttrib(1338): error 0x3009 (EGL_BAD_MATCH)
  10. MAT的 thread overview 功能