news 2026/6/7 12:16:13

C++ 中emplace系列函数

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
C++ 中emplace系列函数
  • emplace的原地构造核心是定位 new(placement new):在容器已分配的内存地址上,直接调用元素的构造函数创建对象;
  • 借助完美转发传递构造参数,自动匹配元素的对应构造函数,无需提前创建临时对象;
  • 相比push系列函数,emplace避免了临时对象的创建、拷贝 / 移动和销毁,效率更高(尤其适合不可拷贝对象如std::thread、大对象如std::function)。

一、核心结论先明确

emplace系列函数(emplace/emplace_back/emplace_front等)确实是直接调用元素的构造函数,并且是在容器为元素分配好的内存空间里 “就地” 构造,全程不会创建临时对象,这也是它比push/push_back更高效的核心原因。

二、emplace 的原地构造实现原理

要理解原地构造,我们先对比push_backemplace_back的区别,再拆解底层实现:

1. 先看 “非原地构造” 的例子(push_back)

比如给vector<std::thread>添加元素时用push_back

// 错误示例:std::thread不可拷贝,push_back会先创建临时thread,再尝试移动/拷贝 // 即使能移动,也会多一次临时对象的创建+销毁 std::vector<std::thread> vec; vec.push_back(std::thread([]() { /* 线程逻辑 */ }));

push_back的执行流程:

  1. 先在当前代码行创建一个临时的std::thread对象(调用std::thread的构造函数);
  2. 容器(vector)为新元素分配内存;
  3. 将临时对象移动 / 拷贝到容器分配的内存中;
  4. 销毁临时对象(调用std::thread的析构函数)。

简单说:push_back是 “先造好对象→再搬到容器里”,中间多了临时对象的开销。

2. 再看 “原地构造” 的例子(emplace_back)

还是上面的场景,用emplace_back

std::vector<std::thread> vec; // emplace_back直接在vector的内存里构造thread对象 vec.emplace_back([]() { /* 线程逻辑 */ });

emplace_back的执行流程:

  1. 容器(vector)先为新元素分配好内存空间(确定内存地址);
  2. emplace_back接收构造std::thread所需的参数(这里是 lambda),通过完美转发std::forward)把参数传递到第一步分配的内存地址上;
  3. 定位 new(placement new)在该内存地址上直接调用std::thread的构造函数,创建对象;
  4. 全程无临时对象,无拷贝 / 移动。
3. 底层关键:定位 new(placement new)

emplace的原地构造核心依赖 C++ 的定位 new语法,它的作用是 “在指定的内存地址上创建对象”,语法形式很简单:

// 普通new:分配内存 + 构造对象 T* ptr = new T(参数); // 定位new:仅在已分配的内存上构造对象(不分配新内存) void* mem = 已分配的内存地址; T* obj = new (mem) T(参数); // 直接在mem指向的地址调用T的构造函数

容器的emplace函数内部,就是用这种方式:先通过容器的内存管理逻辑(比如 vector 的扩容、queue 的节点分配)拿到一块空内存,再用定位 new 调用元素的构造函数,把对象 “造” 在这块内存上。

4. 结合线程池代码的例子

这两行emplace,都是典型的原地构造:

// 1. workers.emplace_back([this]() { ... }) workers.emplace_back([this]() { /* 线程逻辑 */ }); // 原理:vector先为新的thread分配内存,然后直接在该内存上调用std::thread的构造函数(参数是lambda),无临时thread对象 // 2. tasks.emplace(std::move(task)) tasks.emplace(std::move(task)); // 原理:queue先为新的std::function<void()>分配节点内存,然后直接在该内存上调用std::function的移动构造函数,无临时function对象

三、emplace 为什么能匹配任意构造函数?

你可能会问:emplace怎么知道要调用元素的哪个构造函数?答案是完美转发(std::forward)emplace系列函数都是模板函数,会接收任意数量、任意类型的参数,然后通过std::forward把参数 “原样” 传递给元素的构造函数,自动匹配对应的构造版本。

比如:

  • 如果你传[](){}emplace_back,就匹配std::thread的 “接收可调用对象” 的构造函数;
  • 如果你传std::move(task)emplace,就匹配std::function的移动构造函数;
  • 如果你传10, "hello"emplace,就匹配元素的 “接收 int+const char*” 的构造函数(如果有的话)。
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/29 2:20:14

2601,写个kde语法高亮文件

提示:如果想写一个高亮语法文件,XMLCompletion插件可能会很有帮助. 这里概述了KDE4中高亮定义XML格式.基于如下,它将描述主要组成部分及其含义和用法. Kate高亮定义文件的主要部分 在language.dtd文件中也就是DTD,保存正式定义,应该在你的系统目录安装,即$KDEDIR/share/apps/ka…

作者头像 李华
网站建设 2026/5/31 20:36:52

2601C++,复制超文本格式

超文本的剪切板格式 超文本有自己的剪切板格式(叫超文本格式(CF_HTML),可用来向其他应用助手(如Excel,Word或其他办公应用复制)提供数据. CF_HTML是包含说明,环境和该环境中的片段完全基于文本的格式.生成要发送到剪切板的数据时,必须包含数据说明,以指示剪切板版本及环境和片…

作者头像 李华
网站建设 2026/5/31 15:39:27

OTG数据充电交互讲解

随着科技的飞速发展&#xff0c;智能移动设备已成为我们生活中不可或缺的一部分。而在这些设备的连接与数据传输中&#xff0c;Type-C接口以其高效、便捷的特性逐渐占据了主导地位。OTG&#xff08;On-The-Go&#xff09;技术则进一步扩展了Type-C接口的功能&#xff0c;使得设…

作者头像 李华
网站建设 2026/5/29 10:50:22

OpenClaw:你的个人AI助手,多平台统一控制的革命性方案

在这个AI助手百花齐放的时代&#xff0c;你是否厌倦了在多个平台间来回切换&#xff1f;OpenClaw用一套系统统一了所有沟通渠道&#xff0c;让你真正拥有属于自己的AI助手。 &#x1f99e; 什么是OpenClaw&#xff1f; OpenClaw是一个开源的个人AI助手平台&#xff0c;它可以在…

作者头像 李华
网站建设 2026/5/30 20:44:14

Java Web 房屋交易平台系统源码-SpringBoot2+Vue3+MyBatis-Plus+MySQL8.0【含文档】

摘要 随着互联网技术的快速发展&#xff0c;房地产行业逐渐向数字化转型&#xff0c;传统的房屋交易模式已无法满足用户对高效、透明和便捷服务的需求。线上房屋交易平台的出现&#xff0c;为用户提供了更加多样化的选择&#xff0c;同时也为开发商和中介机构拓宽了销售渠道。然…

作者头像 李华