以下是对 vector 的 push_back 和 emplace_back 方法的详细解释:

一、push_back 方法

  • 功能:

    • push_back 是 std::vector 类的成员函数,用于在向量的末尾添加元素。
    • 当使用 push_back 时,会将元素的副本添加到向量的末尾。
  • 使用示例:

#include <iostream>
#include <vector>


class MyClass {
public:
    MyClass() {
        std::cout << "Default constructor" << std::endl;
    }


    MyClass(const MyClass& other) {
        std::cout << "Copy constructor" << std::endl;
    }


    MyClass(MyClass&& other) {
        std::cout << "Move constructor" << std::endl;
    }


    ~MyClass() {
        std::cout << "Destructor" << std::endl;
    }
};


int main() {
    std::vector<MyClass> vec;


    MyClass obj;


    // 使用 push_back 添加元素
    vec.push_back(obj);


    return 0;
}
  • 代码解释:
    • 当调用 vec.push_back(obj); 时,会发生以下步骤:
      1. 调用 MyClass 的复制构造函数,将 obj 的副本添加到 vec 的末尾。
      2. 如果 MyClass 支持移动语义(即提供了移动构造函数),编译器可能会调用移动构造函数进行优化,以避免不必要的复制。

二、emplace_back 方法

  • 功能:

    • emplace_back 也是 std::vector 的成员函数,用于在向量的末尾直接构造元素。
    • 它通过 std::forward 将参数完美转发给元素的构造函数,避免了额外的复制或移动操作,在适当情况下可以提高性能。
  • 使用示例:

#include <iostream>
#include <vector>


class MyClass {
public:
    MyClass() {
        std::cout << "Default constructor" << std::endl;
    }


    MyClass(const MyClass& other) {
        std::cout << "Copy constructor" << std::endl;
    }


    MyClass(MyClass&& other) {
        std::cout << "Move constructor" << std::endl;
    }


    ~MyClass() {
        std::cout << "Destructor" << std::endl;
    }


    MyClass(int x, int y) {
        std::cout << "Custom constructor with arguments " << x << " and " << y << std::endl;
    }
};


int main() {
    std::vector<MyClass> vec;


    // 使用 emplace_back 直接在向量末尾构造元素
    vec.emplace_back(1, 2);


    return 0;
}
  • 代码解释:
    • 当调用 vec.emplace_back(1, 2); 时,会发生以下步骤:
      1. 直接在 vec 的末尾调用 MyClass 的构造函数,使用 1 和 2 作为参数构造元素,而不是先创建对象,再复制或移动到向量中。

三、性能比较

  • push_back 与 emplace_back 的性能对比:
    • 对于简单类型(如 int、double 等),两者性能差异可能不明显,因为复制或移动的成本较低。
    • 对于复杂类型(如自定义类),emplace_back 可能更优,尤其是当类的复制或移动成本较高时。

四、使用建议

  • 何时使用 push_back:

    • 当你已经有一个对象,并且想要将其副本添加到向量末尾时,可以使用 push_back。
  • 何时使用 emplace_back:

    • 当你想在向量末尾直接构造元素,并且元素的构造函数可以接受所需的参数时,使用 emplace_back 可以避免额外的复制或移动操作,提高性能。

以下是一个综合使用 push_back 和 emplace_back 的示例:

#include <iostream>
#include <vector>


class MyClass {
public:
    MyClass(int x) : data(x) {
        std::cout << "Constructor with parameter: " << data << std::endl;
    }


    MyClass(const MyClass& other) : data(other.data) {
        std::cout << "Copy constructor" << std::endl;
    }


    MyClass(MyClass&& other) : data(std::move(other.data)) {
        std::cout << "Move constructor" << std::endl;
    }


    ~MyClass() {
        std::cout << "Destructor for " << data << std::endl;
    }


    int data;
};


int main() {
    std::vector<MyClass> vec;


    MyClass obj1(1);


    // 使用 push_back 添加对象的副本
    vec.push_back(obj1);


    // 使用 emplace_back 直接在向量末尾构造元素
    vec.emplace_back(2);


    return 0;
}

总结

  • push_back 主要用于将现有对象的副本或移动对象添加到向量末尾,可能会调用复制或移动构造函数。
  • emplace_back 用于在向量末尾直接构造元素,避免了额外的复制或移动操作,通过完美转发参数给元素的构造函数。
  • 在性能敏感的代码中,尤其是处理复杂类型时,优先考虑使用 emplace_back 可以提高性能。

需要注意的是,使用 emplace_back 时,需要确保元素的构造函数可以接受提供的参数,并且在某些情况下,编译器可能会自动优化 push_back 的性能,使其与 emplace_back 接近。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐