emplace_back与push_back异同

2023-11-18

vector的emplace_back与push_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的源码

      void
      push_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只能支持一个。

支持原地构造

 从上面

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

emplace_back与push_back异同 的相关文章

随机推荐

  • 启航2021

    1 企业数字化升级之路 附下载地址 点击标题 快速进入下载页面 近年来创新技术的不断升级迭代和突如其来的新冠疫情对各 各业数字化转型的推动 让企业管理者普遍认同数字化 智能化的企业运营模式 能够有效助 企业抗击 险 提升效能 获得 远发展
  • matlab-knn使用

    play with knn 1 什么是knn 1 1 什么是knn 1 2 knn适用范围 2 knn实验前的准备 2 1 knn的实验要点 3 knn的matlab实验 使用UCI数据集 3 0 KNN函数自带用例 3 1 UCI数据集
  • 3.docker仓库(Nexus、Harbor)的安装

    本文目录 前言 1 Aliyun 镜像仓库 2 Nexus 1 Nexus 私服搭建 2 登录控制台 3 配置nexus仓库 4 配置nexus仓库地址为安全的镜像地址 5 镜像推送至nexus仓库 6 拉取nexus仓库镜像 3 Harb
  • c++顺序表与链表的区别

    C Lists 链表 Lists将元素按顺序储存在链表中 与 向量 vectors 相比 它允许快速的插入和删除 但是随机访问却比较慢 说起这个访问速率呢 给大家举个很简单的例子 向量 vectors 也就是顺序表 它的存储方法就类似于c语
  • Exception in thread "main" java.lang.NoClassDefFoundError: XXX

    Exception in thread main java lang NoClassDefFoundError XXX编译时没有报错 一运行就不听话网上找了一大堆 各种解决方案都不是适合我 但是一片文章列举出了可能产生的原因https bl
  • Cpp关键字破解(三)【volatile】篇

    关键字总结 volatile 文章目录 关键字总结 volatile 0 前言 1 概念 2 作用 3 使用场景 4 volatile成员函数 5 代码体验 0 前言 参考几位前辈博客 汇总整理了一下 C 中volatile关键字的使用详解
  • Node.js文件系统模块——读写文件操作

    文章目录 前言 一 导入fs模块 1 readFile readFileSync 2 writeFile writeFileSync 3 appendFile appendFileSync 二 处理文件路径 1 dirname动态拼接路径
  • mysql的逻辑架构

    以一次查询为例 看一下从客户端发来sql 到执行完 整个过程mysql服务器做了哪些事 从整体上 分为三步 连接 对sql语句进行解析和优化 驱动引擎把数据交由文件系统存储 每一部分具体下来 就是这样一幅图 了解一条sql语句从客户端到服务
  • c语言中“

    一个竖杠 表示运算的或 二个竖杠 表示逻辑的或 c语言的位运算 取反 0取反是1 1取反是0 lt lt 是左移 比如1 lt
  • 系统架构设计师-数据库系统(1)

    目录 一 数据库模式 1 集中式数据库 2 分布式数据库 二 数据库设计过程 1 E R模型 2 概念结构设计 3 逻辑结构设计 三 关系代数 1 并交差 2 投影和选择 3 笛卡尔积 4 自然连接 一 数据库模式 1 集中式数据库 三级模
  • less命令打开两个或多个文件时切换文件的快捷键

    在使用 less 命令查看多个文件时 可以使用快捷键 n 和 p 来切换文件 输入 n 后 将切换到下一个文件 输入 p 后 将切换到上一个文件 如下图 less可以打开两个文件 使用快捷键就可以快速查看 而不用退出后再重新打开另一个文件了
  • 计算机专业PhD申请文书范文,美国统计学博士申请文书范文

    美国统计学博士申请文书范文推荐 美国博士申请文书个人陈述作用十分重要 本文为大家提供了一篇成功获取美国统计学博士申请的PS范文 希望大家可以从这一篇文章中得到一些有用的参考信息 I am applying for acceptance in
  • SQL 连接运算join

    连接运算是 8种关系运算 中的一种 五种JOIN方式 1 INNER JOIN or JOIN 2 OUTER JOIN 2 1LEFT OUTER JOIN or LEFT JOIN 2 2RIGHT OUTER JOIN or RIGH
  • 一图看懂 openpyxl 资料整理+笔记(大全)

    本文由 大侠 AhcaoZhu 原创 转载请声明 链接 https blog csdn net Ahcao2008 一图看懂 openpyxl 资料整理 笔记 大全 摘要 类结构图 一级模块目录 按字序 多级模块 按层级 模块级 doc 及
  • 【已更新代码图表】2023数学建模国赛E题python代码--黄河水沙监测数据分析

    E 题 黄河水沙监测数据分析 黄河是中华民族的母亲河 研究黄河水沙通量的变化规律对沿黄流域的环境治理 气候变 化和人民生活的影响 以及对优化黄河流域水资源分配 协调人地关系 调水调沙 防洪减灾 等方面都具有重要的理论指导意义 附件 1 给出
  • STM32---SPI

    SPI 1 SPI介绍 SPI主要应用在EEPROM FLASH 实时时钟 AD转换器 数字信号处理器 数字信号解码器 4条信号线 MISO 主设备输入 从设备输出引脚 主机从这条信号线读入数据 从机的数据由这条信号线输出到主机 即在这条线
  • 面试必问

    若有收获 请记得分享和转发哦 对于工作3年左右的Java程序员来说 在面试大厂的过程中 面试官可能不会太关注你做了多少个项目 你的CRUD水平如何 更多的是关注你对某项技术点的理解深度 所以说 工作3年左右的小伙伴一定要把自己的重心放到技术
  • HTML基础标签及其语义

    一 HTML语法规范 1 1 基本语法表述 标签通常都是成对出现 双标签 开始结束标签 br 单标签 1 2 标签关系 包含关系与并列关系 包含关系 父标签 子标签 并列关系 1 3 HTML基本结构标签 骨架标签 页面内容也是在这些基本标
  • 陷波器设计

    中心频率 f c H z f c rm Hz fc Hz 3dB陷波器带宽 f
  • emplace_back与push_back异同

    vector的emplace back与push back 文章目录 vector的emplace back与push back 前言 1 区别总览 2 push back 支持右值引用 不支持传入多个构造参数 总是会进行拷贝构造 3 em