当前位置: 首页 > news >正文

std::vector<>.emplace_back

emplace_back() 详解:C++ 就地构造的效率革命

emplace_back() 是 C++11 引入的容器成员函数,用于在容器尾部就地构造(而非拷贝或移动)元素。这一特性显著提升了复杂对象的插入效率,尤其适用于构造代价较高的类型。

一、核心优势:就地构造,避免拷贝

传统的 push_back() 需要先构造一个临时对象,再将其拷贝或移动到容器中:

std::vector<std::string> vec;
vec.push_back("hello");  // 步骤1: 构造临时 string 对象// 步骤2: 移动临时对象到 vector 中// 步骤3: 销毁临时对象

emplace_back() 直接在容器尾部的内存空间中构造对象:

vec.emplace_back("hello");  // 直接在 vector 内存中构造 string 对象// 无需临时对象,无需拷贝/移动

二、参数与原理

emplace_back() 的原型为:

template <class... Args>
void emplace_back(Args&&... args);
  • 参数Args&&... args 是一个可变参数模板,接受任意数量和类型的参数
  • 原理:通过完美转发(Perfect Forwarding)将参数传递给元素类型的构造函数
  • 效果:直接在容器管理的内存中构造对象,无需临时对象

三、示例对比

1. 基本类型示例
std::vector<int> vec;
vec.push_back(42);          // 拷贝 int 值
vec.emplace_back(42);       // 直接构造 int 值// 两者效率相同,因为 int 是 POD 类型
2. 复杂对象示例
class ExpensiveObject {
public:ExpensiveObject(int x, double y) : x(x), y(y) {// 复杂且耗时的初始化操作}// 拷贝构造函数(代价高)ExpensiveObject(const ExpensiveObject& other) = delete;// 移动构造函数(代价高)ExpensiveObject(ExpensiveObject&& other) = delete;
};std::vector<ExpensiveObject> vec;// 错误:无法使用 push_back,因为需要拷贝或移动
// vec.push_back(ExpensiveObject(1, 2.0));// 正确:emplace_back 直接构造对象
vec.emplace_back(1, 2.0);  // 直接传递构造参数

四、完美转发与参数匹配

emplace_back() 支持直接传递构造所需的参数,包括:

  1. 构造函数参数
  2. 初始化列表
  3. 隐式类型转换参数
class Person {
public:Person(std::string name, int age) : name(name), age(age) {}private:std::string name;int age;
};std::vector<Person> people;// 使用 emplace_back 传递构造参数
people.emplace_back("Alice", 30);  // 直接构造 Person 对象// 使用 push_back 需要显式构造 Person
people.push_back(Person("Bob", 25));  // 先构造临时对象,再移动

五、与 push_back() 的关键区别

特性emplace_back()push_back()
参数接受构造函数的参数包接受已构造的对象(左值或右值)
构造方式就地构造,无需临时对象需要先构造临时对象,再拷贝/移动
支持不可移动类型支持(只要构造函数可用)不支持(必须可拷贝或可移动)
隐式类型转换支持(直接传递转换所需参数)需显式转换(或提供转换构造函数)

六、注意事项

  1. 内存扩容:若容器需要重新分配内存,emplace_back() 仍需移动所有现有元素

  2. 异常安全:若构造函数抛出异常,容器状态保持不变

  3. 返回值emplace_back() 不返回新元素的引用(C++17 起 emplace() 返回)

  4. 优先使用场景

    • 插入复杂对象(如包含动态资源的类)
    • 插入需要隐式类型转换的对象
    • 插入不可拷贝/不可移动的对象

七、进阶应用:初始化列表参数

emplace_back() 可以正确处理初始化列表参数:

std::vector<std::vector<int>> matrix;// 使用 emplace_back 和初始化列表
matrix.emplace_back({1, 2, 3});  // 直接构造内部 vector// 等价于
matrix.push_back(std::vector<int>{1, 2, 3});

八、性能测试对比

以下代码对比了 push_backemplace_back 的性能差异:

#include <chrono>
#include <vector>
#include <string>
#include <iostream>struct ExpensiveToCopy {std::string largeData;ExpensiveToCopy(const char* data) : largeData(data) {}// 模拟高代价的拷贝构造ExpensiveToCopy(const ExpensiveToCopy& other) : largeData(other.largeData) {// 模拟耗时操作for (int i = 0; i < 1000; ++i) {}}
};int main() {std::vector<ExpensiveToCopy> vec;// 测试 push_backauto start = std::chrono::high_resolution_clock::now();for (int i = 0; i < 10000; ++i) {vec.push_back("a very long string that needs to be copied");}auto end = std::chrono::high_resolution_clock::now();std::cout << "push_back time: " << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count()<< " ms" << std::endl;// 测试 emplace_backvec.clear();start = std::chrono::high_resolution_clock::now();for (int i = 0; i < 10000; ++i) {vec.emplace_back("a very long string that needs to be copied");}end = std::chrono::high_resolution_clock::now();std::cout << "emplace_back time: " << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count()<< " ms" << std::endl;return 0;
}

九、总结

emplace_back() 是 C++ 容器库的重要改进,它通过就地构造机制显著提升了插入效率,尤其适用于:

  1. 构造代价高昂的对象
  2. 需要隐式类型转换的对象
  3. 不可拷贝/不可移动的对象

在现代 C++ 编程中,建议优先使用 emplace_back() 替代 push_back(),除非需要明确的类型检查或兼容性保证。

相关文章:

  • flutter设置最大高度,超过最大高度时滑动显示
  • 使用frp内网穿透本地的虚拟机
  • spring event事件(四)内部事件(1)ApplicationReadyEvent
  • 介绍Buildroot
  • 2025ICPC南昌邀请赛题解
  • 记录学习的第三十六天
  • ZYNQ Cache一致性问题解析与实战:从原理到创新优化
  • PEFT简介及微调大模型DeepSeek-R1-Distill-Qwen-1.5B
  • mysql不能聚合之数据清洗逗号
  • 第7天-Python+PyEcharts实现股票分时图实战教程
  • OD 算法题 B卷 【需要打开多少监视器】
  • 算法打卡第一天
  • 每日算法刷题Day10 5.19:leetcode不定长滑动窗口求最长/最大4道题,结束定长滑动窗口,用时1h
  • 大模型的开发应用(三):基于LlaMAFactory的LoRA微调(上)
  • CSS之box-sizing、图片模糊、计算盒子宽度clac、(重点含小米、进度条案例)过渡
  • 再议AOI算法
  • 谈谈mysql的日志的用途
  • Google精准狙击OpenAI Codex,发布AI编程助手Jules!
  • Kubernetes在线练习平台深度对比:KillerCoda与Play with Kubernetes
  • Rofin PowerLine E Air维护和集成手侧激光Maintenance and Integration Manual
  • 重庆黔江一足疗养生馆负责人涉嫌违法犯罪被移送检察机关
  • 墨西哥城市长私人秘书及顾问遇袭身亡
  • 王毅同巴基斯坦副总理兼外长达尔会谈
  • 盲人不能刷脸认证、营业厅拒人工核验,央媒:别让刷脸困住尊严
  • 墨西哥海军一载两百余人帆船撞上纽约布鲁克林大桥,多人落水
  • 上海公办小学验证今起开始,下周一和周二分区进行民办摇号