vector的push_back v.s. emplace_back

vector的push_back v.s. emplace_back

正文

在 C++ 的 std::vector 中,emplace_backpush_back 都用于向容器末尾添加元素,但它们的实现方式和性能特点有明显差异。以下是两者的核心区别:


1. 参数传递与构造方式

方法 参数类型 构造行为
push_back 已构造的对象 接受一个对象,通过 拷贝或移动 到容器末尾。
emplace_back 构造参数(直接传递参数) 直接在容器内存中 原地构造对象,无需临时对象。

2. 性能差异

  • push_back

    • 若参数是临时对象(如右值),会触发 移动构造(如果对象可移动)。
    • 若参数是左值,会触发 拷贝构造
    • 示例:
      std::vector<MyClass> vec;
      MyClass obj(1, 2, 3);
      vec.push_back(obj);          // 拷贝构造
      vec.push_back(MyClass(4,5,6)); // 移动构造(临时对象)
      
  • emplace_back

    • 直接通过参数构造对象避免临时对象的创建,性能更优。
    • 示例:
      std::vector<MyClass> vec;
      vec.emplace_back(1, 2, 3); // 直接调用 MyClass(int, int, int) 构造函数
      

3. 适用场景

场景 推荐方法
添加已存在的对象(左值) push_back
添加临时对象(右值) push_back + std::move
直接构造对象(避免临时对象) emplace_back
对象不可拷贝/不可移动 必须用 emplace_back

4. 语法对比

// push_back:需要构造临时对象
vec.push_back(MyClass(a, b, c));

// emplace_back:直接传递构造参数
vec.emplace_back(a, b, c);

5. 特殊情况与注意事项

  1. 隐式类型转换

    • push_back 可能因类型不匹配而失败,而 emplace_back 允许隐式调用构造函数。
      struct MyClass {
          explicit MyClass(int x) {} // explicit 构造函数
      };
      
      std::vector<MyClass> vec;
      vec.push_back(10);    // 错误:不能隐式转换 int 到 MyClass
      vec.emplace_back(10); // 正确:直接调用 MyClass(int)
      
  2. 性能陷阱

    • 若对象本身构造代价低(如内置类型 int, double),emplace_backpush_back 性能差异可忽略。
    • 对复杂对象(如包含动态内存的类),emplace_back 通常更高效。
  3. 异常安全性

    • emplace_back 可能在构造过程中抛出异常,此时容器保持原状(强异常安全保证)。

总结

  • 优先使用 emplace_back:当需要直接通过参数构造对象时(尤其是对象构造成本高或不可拷贝/移动时)。
  • 使用 push_back:当已有对象或需要显式控制拷贝/移动行为时。

通过合理选择二者,可以显著优化代码性能并简化逻辑。

posted @ 2025-02-07 12:19  Gold_stein  阅读(61)  评论(0编辑  收藏  举报