vector的emplace_back与push_back

文章目录

  • vector的emplace_back与push_back
    • 前言
    • 1.区别总览
    • 2.push_back
      • 支持右值引用
      • 不支持传入多个构造参数
      • 总是会进行拷贝构造
    • 3.emplace_back
      • emplace_back可以接受`多个构造参数`
      • 支持原地构造

前言

 在vector中,通过push_backemplace_back都可以向尾部添加元素,用push_back也可以,用emplace_back也可以,只看最终造成的结果的话,似乎没有什么不同。我们总是能听到有人说emplace_back比push_back要,但是快在哪里呢? 什么情况下都快吗? 我们一起来跟着例子看一下。

1.区别总览

push_back emplace_back
是否支持右值引用 支持 支持
是否一定会发生拷贝构造 一定 不一定
是够支持直接传入多个构造参数 支持一个构造参数 支持多个构造参数
是够支持原地构造 不支持 支持

 下面对这些特性分别进行分析

2.push_back

支持右值引用

 印象中,push_back是不支持右值引用的,然而实际上,push_back一样支持右值引用,比如我们看push_back的源码

      voidpush_back(value_type&& __x){ emplace_back(std::move(__x)); }template<typename... _Args>

 又或者我们通过代码测试一下

    std::string s1 = "hello";vector<std::string> strVec;strVec.push_back(std::move(s1));cout<<"s1="<<s1<<endl;cout << "strVec.size=" << strVec.size()<<endl;cout << "strVec[0]=" << strVec[0] << endl;

控制台输出

s1=
strVec.size=1
strVec[0]=hello

 std::move将s1从一个左值转化为一个右值引用,传入到push_back作为参数,push_back接收了该参数并调用了std::string移动拷造函数,将资源转移到了容器内。这里提到了一个概念叫移动构造函数,下面会专门介绍这个概念。

不支持传入多个构造参数

 如果只需要传入一个构造参数就能构造对象 ,那么,可以直接在push_back传入这个构造参数,完成对象的构造,例如

class testClass{public:int m_a;int m_b;testClass(int a){m_a = a;m_b = 10;}testClass(int a, int b ){m_a = a;m_b = b;}
};
void testContruct(){vector<testClass> list;list.push_back(1);cout<<"list.size="<<list.size()<<endl;cout << "list[0].m_a=" << list[0].m_a << " list[0].m_b=" << list[0].m_b << endl;
}

 执行结果

list.size=1
list[0].m_a=1 list[0].m_b=10

 在只需要一个参数就能构造的情况下,push_back可以接受这个构造参数并调用相应构造方法进行构造。

 如果构造时需要多个构造参数,则只能手动构造一个临时对象,否则编译出错,例如

void testContruct(){//编译错误list.push_back(1,2);
}

 需要这样写

void testContruct(){list.push_back(testClass(1,2));cout << "list.size=" << list.size() << endl;cout << "list[0].m_a=" << list[0].m_a << " list[0].m_b=" << list[0].m_b << endl;s
}

 程序输出

list.size=1
list[0].m_a=1 list[0].m_b=2

 这里与emplace_back是不同的,emplace_back可以接受多构造参数的情况,下面会分析到。

总是会进行拷贝构造

 这点怎么理解呢,我们通过一个例子来感受一下

 首先创建一个类,实现其构造拷贝构造以及析构函数

class BaseClassTwoPara {public:BaseClassTwoPara(int a){m_a = a;m_b = 999;cout << "construct " << m_a << " " << m_b << endl;}BaseClassTwoPara(int a, int b){m_a = a;m_b = b;cout << "construct " << m_a << " " << m_b << endl;}BaseClassTwoPara(const BaseClassTwoPara &rhs){m_a = rhs.m_a;m_b = rhs.m_b;cout << "copy construct " << m_a << " " << m_b << endl;}~BaseClassTwoPara(){cout << "delete " << m_a << " " << m_b << endl;}int m_a, m_b;
};

 然后,创建一个vector,进行测试,分4种情况,

  • 传入一个构造参数,让push_back自己构造对象
void test_TwoPara_push_back_cp()
{vector<BaseClassTwoPara> vl;cout << "test_TwoPara_push_back_cp"<<endl;vl.push_back(888);
}

 控制台输出:

construct 888 999
copy construct 888 999
delete 888 999
test_TwoPara_push_back_cp
delete 888 999

 能看到,emplace_back首先调用BaseClassTwoPara的构造函数,构造一个临时对象,然后调用拷贝构造函数,将这个对象复制到容器中,然后立马析构掉临时对象,程序结束时,析构掉容器内的对象。

 所以,传入一个参数时,是会发生拷贝

  • 传入已经构造好的对象

 向vl中传入一个已经构造好的对象,观察输出

void test_TwoPara_push_back_cp()
{vector<BaseClassTwoPara> vl;BaseClassTwoPara b1(1, 2);vl.push_back(b1);cout << "test_TwoPara_push_back_cp" << endl;
}

 控制台输出

construct 1 2
copy construct 1 2
test_TwoPara_push_back_cp
delete 1 2
delete 1 2

 这里的流程是,b1首先调用两参数的构造函数,构造出一个局部对象,然后将这个对象作为push_back参数传入,v1复制b1对象到容器内,但是不会向上面那个例子那样,把b1析构,因为b1不是临时对象,程序退出时,b1以及vl内的对象被析构。

 所以,传入一个构造好的对象时,push_back会发生拷贝。

  • 传入一个临时对象

 通过BaseClassTwoPara(1,2)构造一个临时对象传入push_back

void test_TwoPara_push_back_cp()
{vector<BaseClassTwoPara> vl;vl.push_back(BaseClassTwoPara(1,2));cout << "test_TwoPara_push_back_cp" << endl;
}

 控制台输出:

construct 1 2
copy construct 1 2
delete 1 2
test_TwoPara_push_back_cp
delete 1 2

 可以看到,临时对象通过构造函数被创建后,vl复制该临时对象,然后析构临时对象,程序结束时,再析构容器内的对象,类似于上面提到的"传入一个构造参数,让push_back自己构造对象"这种情况。

 所以,传入一个临时对象,push_back会发生拷贝。

  • 传右值

 传右值时,会调用BaseClassTwoPara的移动构造函数,其实也是一种拷贝形式,这里会出现两种不同的情况

 (1)当传入一个右值时,v1首先调用普通构构造函数,构造一个BaseClassTwoPara临时对象,然后调用移动构造函数,在容器内构造一个对象,然后析构临时对象,可以下面代码看出。

void test_TwoPara_push_back_cp()
{vector<BaseClassTwoPara> vl;vl.push_back(1);cout << "test_TwoPara_push_back_cp" << endl;
}

 控制台输出

construct 1 999
move copy construct 1 999
delete 0 0
test_TwoPara_push_back_cp
delete 1 999

 (2)当传入一个右值引用时,直接调用移动构造函数,在容器中创建对象

void test_TwoPara_push_back_cp()
{vector<BaseClassTwoPara> vl;BaseClassTwoPara b1(1, 2);vl.push_back(std::move(b1))cout << "test_TwoPara_push_back_cp" << endl;
}
construct 1 2
move copy construct 1 2
test_TwoPara_push_back_cp
delete 0 0
delete 1 2

 所以,不论什么情况,push_back至少发生一次拷贝操作,这里说的拷贝也把移动构造算进来了,因为移动构造也是基于一个已有的对象,来创建一个新的对象。

3.emplace_back

 emplace_back的大部分特性与push_back都是一样的,这一小节只分析不同点。

 两点不同:

emplace_back可以接受多个构造参数

 可以将多个构造参数传入emplace_back,只要实现了对应的构造函数,就可以完成构造,例如

void test_TwoPara_push_back_cp()
{vector<BaseClassTwoPara> vl;vl.emplace_back(888,777);cout << "test_TwoPara_push_back_cp"<<endl;
}

 控制台输出

construct 888 777
test_TwoPara_push_back_cp
delete 888 777

 也就是emplace_back这里直接调用了BaseClassTwoPara两参数的构造函数,构造了一个BaseClassTwoPara对象,而push_back只能支持一个。

支持原地构造

 从上面

emplace_back与push_back异同相关推荐

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

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

  2. emplace_back和push_back对比分析

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

  3. emplace_back和push_back的区别

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

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

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

  5. c++11中emplace_back vs push_back

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

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

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

  7. c++ emplace_back和push_back的异同

    vectorcv::Mat images; c::Mat image=cv::imread(".jpg"); images.emplace_back(image); images. ...

  8. C++性能之战(3)--emplace_back VS push_back

    0. 写在最前面 本文持续更新地址:https://haoqchen.site/2020/01/17/emplace_back-vs-push_back/ std::vector中实现了这两个函数,主 ...

  9. C++ 中emplace_back和push_back差异

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

最新文章

  1. 巧用Stream优化老代码,太清爽了!
  2. python要学多久-python要学多久
  3. Apache Kafka-事务消息的支持与实现(本地事务)
  4. 一篇文章,带你全面的了解二叉树-记得点赞
  5. python 字典字符串转字典——urllib.request.Request发送get,post请求,发送json参数
  6. 计算机和自动化结合的专业,自动化转计算机?你可以选择这个专业!
  7. MySQL 读写分离 部分_一个完整的mysql读写分离环境包括以下几个部分
  8. 杨中科:【我的大学生活】
  9. SPSS数据分析流程
  10. oracle临时表空间占用率过高,ORACLE 临时表空间使用率过高的原因及解决方案
  11. 人工智能案例:车厂特斯拉的花式AI应用...
  12. 无人车传感器 IMU 深入剖析
  13. 抑制广播风暴 各种发包
  14. 动画交互设计与技术实现
  15. Jackson Json 快速入门
  16. Mac如何打开企业微信内置浏览器控制台
  17. flutter拨打电话url_launcher
  18. 基于JTBC的装修公司网站
  19. BIM模型文件下载——某幼儿园设计方案Revit模型
  20. zip包怎么解压oracle,使用jar与zip压缩解压文件的区别

热门文章

  1. 致敬,开源界的大佬们!
  2. html背景图总是在字的下面,怎么我在excel表格里插入背景图片后图片不是显示在文字下面而跑到文字右边的...
  3. excel汇总统计怎么做?
  4. 头肩整理形态(转载)
  5. window.open打开窗口时要使窗口去掉菜单栏、工具栏、标题栏,达到窗口最大化
  6. c语言字符画火箭,用C语言实现火箭的升空
  7. c语言均衡器,关于调音:如何使用动态均衡器?
  8. 面向未来的跨界开发技术(下)
  9. 哈希表——开哈希表和闭哈希表
  10. jsonp 跨域接收值接不到的解决方法