一文弄清楚 push_back 和 emplace_back 的区别

开发 前端
emplace_back 的缺点是代码可读性相对差些,因此,对于往容器尾部添加元素的操作,选择 push_back将会使你的代码可读性更好,能更好的表达出代码编写者的目的,代码更健壮。

在 vector 中有一个 push_back 方法,作用是往容器尾部插入一个元素,后来在 c++11 里面,又加入了一个 emplace_back 方法, 作用和push_back 一样

既然两者功能一样,那它们之间有什么区别呢 ?使用的时候如何选择呢 ?

效率

emplace_back 在效率上比 push_back 要好一些,请看下面的例子:

#include<iostream>
#include <vector>
using namespace std;

class MyTest
{
public:
//普通构造
MyTest(int id,int age):m_id(id),m_age(age)
{
cout << "ceate MyTest class..." << this << endl;
}
//拷贝构造
MyTest(const MyTest &t):m_id(t.m_id),m_age(t.m_age)
{
cout << "copy construct called..." << this << endl;
}
//移动构造
MyTest(const MyTest &&t)
{
m_id = std::move(t.m_id);
m_age = std::move(t.m_age);
cout << "move contruct called.." << this << endl;
}
//析构
~MyTest()
{
cout << "destory MyTest class..." << this << endl;
}
private:
int m_id; //id成员
int m_age;//age成员
};

int main(int argc, char *argv[])
{
vector<MyTest> vec;
vec.reserve(2); //预先分配内存
cout << "\n ------ push_back --------" << endl;
vec.push_back(MyTest(1,20));
cout << "\n ------ emplace_back --------" << endl;
vec.emplace_back(1,20);
cout << "\n -------- finish -------- " << endl;
}

使用 g++ -g -Wall -std=c++11 -o t t.cpp 命令编译,运行程序,结果如下:

 ------ push_back --------
ceate MyTest class...
move contruct called..
destory MyTest class...

------ emplace_back --------
ceate MyTest class...

-------- finish --------
destory MyTest class...
destory MyTest class...

从结果可以看出,同样是在容器尾部加入一个元素,push_back 和 emplace_back 的过程是不一样的;

  • push_back 的过程

构造一个临时对象

调用移动构造函数把临时对象的副本拷贝到容器末尾增加的元素中

调用析构释放临时对象

  • emplace_back 的过程

调用构造函数在容器末尾增加一个元素

同样是在容器尾部增加一个元素,emplace_back 比 push_back 少了一次对象的构造和析构, 所以,emplace_back 比 push_back 更高效, 具体能高效多少呢,这里进行了一个插入 一百万 个对象的测试,emplace_back 比 push_back 快大概 20% ,下面是测试代码 :

//获取当前时间,单位: 毫秒
int64_t cur_msec()
{
struct timespec tp1;
clock_gettime(CLOCK_REALTIME, &tp1);
return (tp1.tv_sec * 1000 + (int64_t)tp1.tv_nsec / 1000000.0);
}
//测试函数
void test()
{
vector<MyTest> vec1;
vector<MyTest> vec2;
vec1.reserve(1000000);
vec2.reserve(1000000);

int64_t t1 = cur_msec();
for (size_t i = 0; i < 1000000; i++)
{
vec1.push_back(MyTest(1,20));
}
int64_t t2 = cur_msec();
for (size_t i = 0; i < 1000000; i++)
{
vec2.emplace_back(1,20);
}
int64_t t3 = cur_msec();

cout << " push_back cost " << (t2 - t1) << " millisecond " << endl;
cout << " emplace_back cost " << (t3 - t2) << " millisecond " << endl;
}

emplace_back 的缺点

既然 emplace_back 比 push_back 更高效,是不是每次都用 emplace_back 就完了呢?

我们日常写代码,除了执行率之外,还要考虑可读性,理解成本等,虽然emplace_back 效率高些,但是它也是有缺点的,比如:

vec1.push_back(1000000);
vec2.emplace_back(1000000);

第一行程序代码很好理解,往 容器vec1尾部加入一个整数 1000000,然而,第二行程序代码就不是很直观了,由于我们不知道 vec2 的实际类型,所以无法获得这行代码执行的结果

假如,vec2 的类型是 vector,那么它的含义和第一行一样,往容器vec2尾部添加整数 1000000

假如,vec2 的类型是 vector< vector >,那么它就构造了一个包含 1000000 个元素的容器,按照每个元素 4 个字节来计算,执行第二行代码需要分配差不多近 4M 的内存空间

如何选择

emplace_back 的缺点是代码可读性相对差些,因此,对于往容器尾部添加元素的操作,选择 push_back将会使你的代码可读性更好,能更好的表达出代码编写者的目的,代码更健壮

像上节中的例子,一个 vector< vector > 类型的容器 vec,vec.emplace_back(1000000) 能编译通过,在运行到这行代码之前可能还无法发现错误,但是如果调用 vec.push_back(1000000) 在编译的时候就会报错,能更早的发现问题

当然,既然 emplace_back 比 push_back 更快,我们也不能因为容易出错就不使用它,在对效率有要求的场景中,推荐使用 emplace_back,当对效率要求没那么高,或者使用 emplace_back 和 push_back 效率差别不大时,从项目代码可读性,可维护性的角度考虑的话,优先使用 push_back。

责任编辑:武晓燕 来源: Linux开发那些事儿
相关推荐

2022-04-07 08:37:05

链表技巧单链表

2012-05-28 10:06:05

项目开发项目管理开发

2018-10-25 09:26:07

VLANVXLAN网络

2017-03-31 15:30:09

2022-05-30 08:05:11

架构

2023-01-09 08:38:22

大数据架构师YARN

2017-09-26 10:36:52

云端部署内部

2024-01-12 08:26:16

Linux磁盘文件系统

2017-10-28 23:00:52

多云混合云云计算

2021-03-11 15:49:44

人工智能深度学习

2021-10-29 11:30:31

补码二进制反码

2024-04-10 13:50:41

CentOSUbuntu操作系统

2009-10-22 10:18:45

Back-to-BacCCIE

2021-03-19 14:12:24

2023-02-26 21:33:49

混合云架构模式

2023-12-28 07:37:24

CAS内存阻塞

2017-07-28 09:11:14

HIVEHBASE区别

2021-05-09 22:26:36

Python函数变量

2020-11-04 07:49:04

Select

2020-11-17 08:32:22

存储器链接
点赞
收藏

51CTO技术栈公众号