首页 > 其他分享 >push_back和 emplace_back背后的逻辑

push_back和 emplace_back背后的逻辑

时间:2024-09-18 21:04:17浏览次数:9  
标签:emplace 对象 back 构造 vector push

push_backemplace_back 的区别

  1. push_back

    • 功能:将一个对象(或其副本)添加到 vector 的末尾。
    • 参数:接受一个对象(或其副本)的引用。
    • 过程
      • 如果传入的是一个临时对象或一个已有对象,push_back 会创建该对象的副本(或者通过移动构造函数将其移动到 vector 中)。
      • 可能会涉及到复制构造函数或移动构造函数,特别是在添加复杂对象时,这可能会带来额外的性能开销。
    • 示例
      std::vector<std::string> vec;
      std::string str = "Hello";
      vec.push_back(str); // 复制 str 到 vector 的末尾
      vec.push_back("World"); // 复制临时字符串到 vector 的末尾
      
  2. emplace_back

    • 功能:在 vector 的末尾直接构造一个对象,避免了不必要的复制或移动。
    • 参数:接受对象构造函数的参数,这些参数会被直接传递给对象的构造函数。
    • 过程
      • 直接在 vector 的末尾构造对象,无需创建临时对象或复制现有对象。
      • 这通常会提高性能,尤其是在构造复杂对象时,因为它避免了不必要的中间步骤。
    • 示例
      std::vector<std::string> vec;
      vec.emplace_back("Hello"); // 直接在 vector 的末尾构造 std::string 对象
      vec.emplace_back("World", 5, 'X'); // 直接在 vector 的末尾构造 std::string 对象,使用构造函数参数
      

让我们用更简单的语言来解释“原地构造”以及它如何通过构造函数参数转发来实现。

原地构造的解释

原地构造是指在一个已经分配好的内存位置上直接创建一个对象。这个过程避免了创建临时对象和额外的复制操作,从而提高效率。

比喻

想象你有一个大空盒子(内存),你已经把这个盒子放在了一个特定的位置(比如一个房间的角落)。你现在要在这个盒子里直接组装一个玩具(对象),而不是先组装一个玩具,然后再把它放到盒子里。这就是原地构造——直接在那个空盒子里组装玩具,省去了中间的步骤。

构造函数参数转发

构造函数参数转发的作用是把你传给构造函数的参数直接传递给对象的构造函数,而不需要先创建一个临时对象。这可以避免不必要的复制,并提高效率。

示例

假设你有一个容器(比如 std::vector),你想要把一个新对象添加到容器中。传统的方法可能是:

  1. 创建一个临时对象:在你把对象放到容器里之前,你首先要创建一个临时对象。
  2. 将临时对象添加到容器:然后把这个临时对象复制到容器中。

这两个步骤可能很耗时间,尤其是当对象比较复杂时。

原地构造通过构造函数参数转发可以简化这个过程:

  • 原地构造:在已分配好的内存位置直接构造对象,避免了不必要的中间步骤和复制。
  • 构造函数参数转发:将参数直接传递给构造函数,使得对象可以在目标位置直接构造,而不需要创建临时对象。

通过原地构造和参数转发可以让程序更加高效,特别是在处理大量数据或复杂对象时。

构造函数参数转发的基本概念

构造函数参数转发是 C++ 中的一种技术,它允许你将传递给一个函数的参数直接传递给另一个构造函数。这通常通过完美转发(perfect forwarding)来实现。完美转发确保了参数的类型和值特性(如左值、右值)能够保持不变,避免了不必要的复制或移动。

逻辑解析

  1. 目标

    • 直接在目标位置构造对象,避免创建临时对象和多次复制。
  2. 过程

    • 构造函数参数:当你调用一个成员函数(例如 emplace_back)时,传递给它的参数是用于构造一个新对象的。
    • 完美转发emplace_back 这种函数会使用完美转发,将这些参数原样传递给目标对象的构造函数。

完美转发的实现

C++ 中的完美转发通常通过模板和 std::forward 实现。下面是一个简单的例子,演示如何通过模板函数来转发参数:

#include <utility> // for std::forward

template <typename T, typename... Args>
void construct(T& obj, Args&&... args) {
    new (&obj) T(std::forward<Args>(args)...); // 使用原地构造
}

在这个例子中,construct 函数使用 std::forward 将参数 args 直接转发给 T 的构造函数。这确保了参数的类型和特性被保留,并且 T 对象会在指定的位置(由 &obj 指定)原地构造。

  • 构造函数参数转发:将函数参数直接传递给另一个构造函数,从而在目标位置直接构造对象。
  • 完美转发:确保传递的参数保持其原始特性(如左值或右值),避免不必要的复制或移动。
  • 原地构造:在已经分配好的内存区域内直接创建对象,减少了中间步骤和复制开销。

使用原地构造和构造函数参数转发可以使程序更加高效,特别是在处理复杂对象和大数据量时。

使用 emplace_back 代替 push_back

  • 性能emplace_back 可以在大多数情况下完全代替 push_back,而且在性能上通常优于 push_back。特别是当你向 vector 添加复杂对象时,emplace_back 可以避免不必要的复制或移动,减少性能开销。

  • 适用性emplace_back 更适用于需要直接在 vector 内部构造对象的情况。例如,当你要创建一个对象并将其添加到 vector 时,emplace_back 允许你直接传递构造函数参数而无需先创建临时对象。对于简单对象(如内置类型或小型 POD 类型),push_backemplace_back 的差异可能不明显,但 emplace_back 仍然是更灵活和高效的选择。

  • 兼容性emplace_back 可以用来代替 push_back,但 push_back 不能完全代替 emplace_back,因为 push_back 需要一个已经构造好的对象(或临时对象),而 emplace_back 允许在 vector 内部直接构造对象,避免了中间步骤。

结论

  • 使用 emplace_back:在可以直接构造对象的情况下,emplace_back 更优,因为它避免了不必要的复制或移动,提升性能。emplace 函数的主要任务是在这些已经分配的内部内存区域中直接构造对象
  • 使用 push_back:在需要传递已有对象时,使用 push_back 是合适的。它在添加现有对象时仍然有效。

总的来说,emplace_backpush_back 的一种更高效的选择,可以在大多数情况下完全代替 push_back。然而,根据实际情况和需求,选择合适的方法可以使代码更简洁和高效。

标签:emplace,对象,back,构造,vector,push
From: https://www.cnblogs.com/niumachen/p/18419313

相关文章

  • antd-Vue 3.X版本 a-back-top使用
    api中例子本地项目中没显示出来原因是没有图标 采用引用图标的方式展示使用的时候需注意:1.target是找到滚动的目标元素,不然也显示不出2.visibilityHeight默认是200滚动不到这个数值可能也显示不出<div><a-back-top:target="targetFunc":visibilityHeight="100">......
  • [官翻]mysqlbackup的乐观备份
    乐观备份可以用来提升备份和恢复体量比较大的数据库(只有少量的表经常变更)的性能。2)在大型数据库的热备份过程中(例如,以TB为单位),当备份进行时,可能会在服务器上生成巨大的重做日志文件。由于重做日志文件的增长速度快于mysqlbackup处理的速度,因此当mysqlbackup无法赶上重做日志周期,并......
  • YoloV8改进策略:BackBone改进|Swin Transformer赋能YoloV8,性能跃升的新篇章
    摘要在深度学习领域,目标检测作为计算机视觉的核心任务之一,其性能的提升始终吸引着研究者们的目光。近期,我们创新性地将SwinTransformer这一前沿的Transformer架构引入到YoloV8目标检测模型中,通过替换其原有的主干网络,实现了检测性能的显著提升,为YoloV8系列模型注入了新的......
  • RT-DETR改进策略:BackBone改进|Swin Transformer,最强主干改进RT-DETR
    摘要在深度学习与计算机视觉领域,SwinTransformer作为一种强大的视觉Transformer架构,以其卓越的特征提取能力和自注意力机制,正逐步引领着图像识别与检测技术的革新。近期,我们成功地将SwinTransformer引入并深度整合至RT-DERT(一种高效的实时目标检测与识别框架)中,通过替换其......
  • Laravel BroadcastAs 中的 Pusher 传递参数
    一、BroadcastAs简介的作用是Laravel框架中的一个特性,用于在广播事件时指定事件的名称。它的作用是提供一种更具可读性和可维护性的方式来标识广播事件。通过使用BroadcastAs,开发人员可以更清晰地表达事件的含义,使得代码更易于理解和维护。此外,BroadcastAs还可以用于在不同的......
  • 深入理解Redis锁与Backoff重试机制在Go中的实现
    目录Redis锁的深入实现Backoff重试策略的深入探讨结合Redis锁与Backoff策略的高级应用具体实现结论在构建分布式系统时,确保数据的一致性和操作的原子性是至关重要的。Redis锁作为一种高效且广泛使用的分布式锁机制,能够帮助我们在多进程或分布式环境中同步访问共享资源。本文将深......
  • 踩坑日志1:UserWarning: Plan failed with a cudnnException: CUDNN_BACKEND_EXECUTION
     在运行深度模型时,遇到了下面有关cuDNN的错误,虽然好像不影响模型训练,但是感觉很烦、有一捏捏代码洁癖。D:\anaconda\envs\myPytorch\Lib\site-packages\torch\autograd\graph.py:744:UserWarning:PlanfailedwithacudnnException:CUDNN_BACKEND_EXECUTION_PLAN_DESCRIPT......
  • commit@push 与 commit@sysc 有什么区别和联系
    commit@push和commit@sync是Git操作中的两种不同命令组合或工作流,虽然两者都涉及commit操作,但它们的目标和执行操作有些不同。下面解释它们的区别和联系:1.commit@push含义:指的是先进行gitcommit提交,然后使用gitpush将本地的提交推送到远程仓库。流程:commit......
  • github push项目
    新建项目进入项目文件夹,打开gitbash执行ls检查文件目录执行gitinit初始化执行gitadd.把文件都加到缓存区执行gitcommit-m"添加说明(注意代码规范)"gitbranch-Mmain重命名当前分支为miangitremoteaddoriginhttps://github.com/用户没/项目名.git向本地Git仓库......
  • chainlit 持久化配置问题 null value in column "disableFeedback" of relation "ste
    实际上此问题在github上已经存在了,解决方法很简单,就是对于sql配置的去掉不能为空的判定参考sql修改CREATETABLEIFNOTEXISTSsteps("id"UUIDPRIMARYKEY,"name"TEXTNOTNULL,"type"TEXTNOTNULL,"threadId"UUIDNOTNULL,"parentId"UUID,&qu......