emplace方法原理剖析

2023-05-16

emplace_back 和 push_back 的差别

有一个类Test定义如下

class Test
{
public:
	Test(int a) { cout << "Test(int a)" << endl; }
	Test(int a, int b) { cout << "Test(int a, int b)" << endl; }
	~Test() { cout << "~Test()" << endl; }
	Test(const Test &) { cout << "Test(const Test &)" << endl; }
	Test(Test &&) { cout << "Test(Test &&)" << endl; }
};

case1:直接插入对象,emplace_back 和 push_back 没有区别

Test t1(10);
vector<Test> v;
v.reserve(100);
// 直接插入对象,两个是没有区别的
v.push_back(t1);
v.emplace_back(t1);

输出结果:

Test(const Test &)

Test(const Test &)

case2:插入临时对象

都是先调用构造创建临时对象,再调用移动构造(接受右值)

若没有移动构造则调用拷贝构造

v.push_back(Test(20));
v.emplace_back(Test(20));

输出结果

Test(int a)

Test(Test &&)

~Test()

Test(int a)

Test(Test &&)

~Test()

case3:直接传入构造函数的所需参数,进行隐式类型转换调用

v.push_back(20);  
cout << "============" << endl;
v.emplace_back(20); 

输出结果

Test(int a)

Test(Test &&)

~Test()

============

Test(int a)

分析

push_back先调用构造创建临时对象(隐式类型转换,因为没有接受一个int的push_back重载函数),再调用移动构造;

emplace_back只调用了构造函数;

可以看出节省了临时对象的析构以及移动(拷贝)构造函数的调用;

相同的还有mapinsert 该换为 `emplace方法也可以节省一次移动构造(拷贝构造的开销)

简单实现 vector 的 emplace_back

核心思路(重要)

实现这一省去临时对象创建和拷贝调用的方法可以提供一个接受构造函数参数类型变量emplace_back ,让 emplace_back 拿这这些参数直接去调用对应的构造函数。

push_back之所以会产生临时对象创建和拷贝调用,是因为其只有接受容器对象类型的参数的重载版本,传递对象构造函数的参数时。要先隐式转换就会调用构造产生临时对象再调用拷贝。

引用折叠使得引用类型形参可接受左值和右值,再利用完美转发使得左值引用和右值引用的函数能合并到一个函数中

完整代码:

template<typename T>
struct MyAllocator
{
	// allocate deallocate
	T* allocate(size_t size)
	{
		return (T*)malloc(size * sizeof(T));
	}
	template<typename... Types>
	void construct(T *ptr,Types&&... args)
	{
		// 类型完美转发,否则都是左值
		new (ptr) T(std::forward<Types>(args)...);
	}
};

template<typename T, typename Alloc = MyAllocator<T>>
class Vector
{
public:
	Vector() : vec_(nullptr), size_(0), idx_(0) {}
	void reserve(size_t size) // 开空间,但不构造对象
	{
		vec_ = allocator_.allocate(size);
		size_ = size;
	}
	
	// 这个push_back传入构造函数参数隐式转换时就会造成构造临时对象和调用拷贝的情况
	void push_back(const T& val)
	{
		allocator_.construct(vec_ + idx_, val);
		idx_++;
	}
	void push_back(T &&val)
	{
		allocator_.construct(vec_ + idx_, std::move(val));
		idx_++;
	}
	/*template<typename Type>
	void push_back(Type&& val)
	{
		allocator_.construct(vec_ + idx_, std::forward(val));
		idx_++;
	}*/
	

	// 引用折叠,&& + && = &&, & + && = &
	template<typename... Types>
	void emplace_back(Types&&... args)  // 这样使得移动构造可以少调用一次参数,因为能接受构造函数参数类型的变量
	{
		// args 中一系列参数传入的可能是左值或是右值,但是接受之后有了名字就都是左值
		// 要完美转发保持引用类型
		allocator_.construct(vec_ + idx_, std::forward<Types>(args)...);
		idx_++;
	}
private:
	T* vec_; // 底层用动态数组
	int size_;
	int idx_;
	Alloc allocator_;

};

上面代码重点关注push_backemplace_back 的写法

测试代码

class Test
{
public:
	Test(int a) { cout << "Test(int a)" << endl; }
	Test(int a, int b) { cout << "Test(int a, int b)" << endl; }
	~Test() { cout << "~Test()" << endl; }
	Test(const Test &) { cout << "Test(const Test &)" << endl; }
	Test(Test &&) { cout << "Test(Test &&)" << endl; }
};

int main()
{ 
	Test t1(10);
	Vector<Test> v;
	v.reserve(100);

	cout << "============" << endl;
	// 直接插入对象,两个是没有区别的
	v.push_back(t1);
	v.emplace_back(t1);
	cout << "============" << endl;
	// 都是先调用构造创建临时对象,再调用移动构造(接受右值)
	// 若没有移动构造则调用拷贝构造
	v.push_back(Test(20));
	v.emplace_back(Test(20));
	cout << "============" << endl;
	v.push_back(20);  // 先调用构造创建临时对象,再调用移动构造
	v.emplace_back(20); // 只调用了构造函数
	cout << "============" << endl;
	return 0;
}

输出结果和STL的vector的效果相同:

Test(int a)

============

Test(const Test &)

Test(const Test &)

============

Test(int a)

Test(Test &&)

~Test()

Test(int a)

Test(Test &&)

~Test()

============

Test(int a)

Test(Test &&)

~Test()

Test(int a)

============

~Test()

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

emplace方法原理剖析 的相关文章

  • Windows下切换不同版本JDK

    1 前言 从四月份重新入职新公司以来 xff0c 主要负责两个项目的开发 xff0c 一个是10多年前的项目 xff0c 一个是2019年开始开发的项目 xff0c 这两个项目依赖于不同版本的JDK xff0c 一个是JDK6 xff0c
  • css实现圆形div旋转,如“已预约”效果

    lt DOCTYPE html gt lt html gt lt head gt lt meta charset 61 34 utf 8 34 gt lt title gt lt title gt lt head gt lt style g

随机推荐

  • c++---类和对象(六大默认成员函数)

    类中默认的六个成员函数构造函数析构函数拷贝构造函数赋值操作符重载取地址和const取地址操作符重载const成员函数 1 类中默认的六个成员函数 首先看看下面代码 class A int main A a return 0 这个代码并没有报
  • 微服务系列--nacos注册中心与服务发现

    1 前言 终究还是到了更新关于微服务相关博客的时候了 xff0c 经过挺长一段时间微服务的自主学习 xff0c 现在不敢说自己熟悉微服务 xff0c 但我也能略知一二 微服务嘛 xff0c 其实入门之后便会发现 xff0c 其实关于微服务相
  • 微服务系列--Ribbon负载均衡

    1 前言 这篇文章接上一篇文章进行开发 xff0c 上一篇整合完了Nacos xff0c 这篇来整合Ribbon Ribbon不属于SpringCloud Alibaba的东西 xff0c 而是基于Netflix Ribbon实现的 可以让
  • Centos7安装Jenkins

    将Jenkins存储库添加到yum repos xff0c 并安装Jenkins sudo wget O etc yum repos d jenkins repo http pkg jenkins ci org redhat jenkins
  • Jenkins自动化部署SpringBoot项目

    首先需要安装所需的两个插件 xff0c Maven Integration plugin 和 Publish Over SSH 在 系统配置 xff0c 将服务器信息配置到jenkins xff0c 我用的是腾讯云服务器 xff0c 所以将
  • 优雅关闭SpringBoot项目-接口方式

    前言 一般在服务器重新部署SpringBoot项目 xff0c 无非就是用kill 9暴力停止进程 xff0c 但会造成很多数据问题 xff0c 如果遇到一些耗时或者正在处理交易类的业务时 xff0c 直接导致数据异常 xff0c 严重会导
  • Linux安装Jenkins

    前言 现在Jenkins的最新版本都需要基于JDK11以上才能够正常使用 xff0c 不然会出现各种插件安装不上的问题 又不想安装JDK11 xff0c 想继续用JDK8 xff0c 只能通过指定安装符合JDK8的Jenkins版本 PS
  • rosdep update出错解决办法(2021)

    ROS安装方法 xff1a ros安装后 xff0c 初始化时rosdep update出错解决办法 2021 06 30 初始化时rosdep update出错解决办法 2021年以前 xff0c 通过科学上网 手机开热点等方式 xff0
  • 0x0FA23729 (vcruntime140d.dll)处(位于 类和对象-封装.exe 中)引发的异常(已解决)

    运行程序的时候第42行 抛出异常 xff0c 但是我将该cpp文件放到别的解决方案下就不会出异常 include lt iostream gt include lt string gt using namespace std class P
  • Win10 摄像头:由于其配置信息(注册表中的)不完整或已损坏,Windows无法启动这个硬件设备.【未解决完全】

    问题描述 xff1a 刚刚重装完win10系统之后 xff0c 出现无法打开摄像头的问题 xff0c 解决方法 xff1a 通过修改注册表中得相关信息进行解决 首先打开设备管理器 xff0c 找到设备的类Guid记录下Guid 的值 如此处
  • 2021年总结与2022年展望

    一 工作和学习 通过调剂 xff0c 正式成为了一名研究生 xff0c 结束了自己两年的考研备考 xff0c 不知道是好还是坏 xff0c 看到周围朋友考的时候心里还是会有点失落感 在一家通信设备公司实习软件测试 xff0c 收获很多 xf
  • 计算机网络---网络基础(TCP/IP五层模型,数据的封装和分用)

    认识网络中常用的名词以及基本的概念熟悉OSI七层模型和TCP IP五层模型理解网络通信的数据传输流程 认识网络中常用的名词 ip地址 ip地址就是表示我们一台主机的因为数字不好记忆 xff0c 通常使用点分十进制表示IP xff0c 每条数
  • C语言简易TCP服务端程序

    C语言TCP服务端程序 文章目录 C语言TCP服务端程序项目介绍关键技术代码实现一请求一线程方式epoll方式实现多个客户端连接的TCP服务端程序epoll的水平触发和边缘触发 完整代码编译和启动使用NetAssist测试 项目介绍 本项目
  • C++使用gRPC实例

    什么是gRPC RPC 即远程过程调用协议 xff08 Remote Procedure Call Protocol xff09 xff0c 可以让我们像调用本地对象一样发起 远程调用 RPC 凭借其强大的治理功能 xff0c 成为解决分布
  • 复睿智行CC++开发实习面试

    线上面试 xff0c HR和一位技术的面试官 自我介绍 现在研究生学习的方向是什么 xff1f 我还去答区块链 xff0c 本来就半桶水 xff0c 还不如直接回答C C 43 43 后端这个方向 大数据分析这一块有做过吗 xff1f 我
  • 经纬恒润LinuxC++日常实习面经

    自我介绍 在学校成绩如何 xff0c 有没有获得奖学金 xff0c 考研的时间等等相关问题 能实习多久 xff0c 研究生研究的方向 你这个LinuxC 43 43 开发的学习是自学的吗 xff0c 怎样的自学途径 我 xff1a 看书 看
  • 集群聊天服务器项目(三)——负载均衡模块与跨服务器聊天

    负载均衡模块 为什么要加入负载均衡模块 原因是 xff1a 单台服务器并发量最多两三万 xff0c 不够大 负载均衡器 Nginx的用处或意义 xff08 面试题 xff09 把client请求按负载算法分发到具体业务服务器Chatserv
  • C++校招面试题

    C 43 43 static关键字的作用 xff08 从elf结构 链接的过程 xff09 答 xff1a static可以修饰全局变量 函数 局部变量 xff0c 这些符号在加了staitc后就只能在当前文件可见 xff0c 其他文件不可
  • make_shared知识点

    背景 普通创建shared ptr的方法如 xff1a shared ptr span class token operator lt span span class token keyword int span span class to
  • emplace方法原理剖析

    emplace back 和 push back 的差别 有一个类Test定义如下 span class token keyword class span span class token class name Test span span