2026/5/21 12:19:31
网站建设
项目流程
上海网站关键词排名,帝国网站搬家教程,2023中国互联网公司排行榜,国家建设协会官方网站emplace的原地构造核心是定位 new#xff08;placement new#xff09;#xff1a;在容器已分配的内存地址上#xff0c;直接调用元素的构造函数创建对象#xff1b;借助完美转发传递构造参数#xff0c;自动匹配元素的对应构造函数#xff0c;无需提前创建临时对象placement new在容器已分配的内存地址上直接调用元素的构造函数创建对象借助完美转发传递构造参数自动匹配元素的对应构造函数无需提前创建临时对象相比push系列函数emplace避免了临时对象的创建、拷贝 / 移动和销毁效率更高尤其适合不可拷贝对象如std::thread、大对象如std::function。一、核心结论先明确emplace系列函数emplace/emplace_back/emplace_front等确实是直接调用元素的构造函数并且是在容器为元素分配好的内存空间里 “就地” 构造全程不会创建临时对象这也是它比push/push_back更高效的核心原因。二、emplace 的原地构造实现原理要理解原地构造我们先对比push_back和emplace_back的区别再拆解底层实现1. 先看 “非原地构造” 的例子push_back比如给vectorstd::thread添加元素时用push_back// 错误示例std::thread不可拷贝push_back会先创建临时thread再尝试移动/拷贝 // 即使能移动也会多一次临时对象的创建销毁 std::vectorstd::thread vec; vec.push_back(std::thread([]() { /* 线程逻辑 */ }));push_back的执行流程先在当前代码行创建一个临时的std::thread对象调用std::thread的构造函数容器vector为新元素分配内存将临时对象移动 / 拷贝到容器分配的内存中销毁临时对象调用std::thread的析构函数。简单说push_back是 “先造好对象→再搬到容器里”中间多了临时对象的开销。2. 再看 “原地构造” 的例子emplace_back还是上面的场景用emplace_backstd::vectorstd::thread vec; // emplace_back直接在vector的内存里构造thread对象 vec.emplace_back([]() { /* 线程逻辑 */ });emplace_back的执行流程容器vector先为新元素分配好内存空间确定内存地址emplace_back接收构造std::thread所需的参数这里是 lambda通过完美转发std::forward把参数传递到第一步分配的内存地址上用定位 newplacement new在该内存地址上直接调用std::thread的构造函数创建对象全程无临时对象无拷贝 / 移动。3. 底层关键定位 newplacement newemplace的原地构造核心依赖 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::functionvoid()分配节点内存然后直接在该内存上调用std::function的移动构造函数无临时function对象三、emplace 为什么能匹配任意构造函数你可能会问emplace怎么知道要调用元素的哪个构造函数答案是完美转发std::forwardemplace系列函数都是模板函数会接收任意数量、任意类型的参数然后通过std::forward把参数 “原样” 传递给元素的构造函数自动匹配对应的构造版本。比如如果你传[](){}给emplace_back就匹配std::thread的 “接收可调用对象” 的构造函数如果你传std::move(task)给emplace就匹配std::function的移动构造函数如果你传10, hello给emplace就匹配元素的 “接收 intconst char*” 的构造函数如果有的话。