欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

C++11 之 emplace_back() 与 push_back() 的区别

程序员文章站 2022-03-21 19:29:06
...

参考:

  1. http://c.biancheng.net/view/6826.html
  2. https://www.jianshu.com/p/1fb2daf66582?native.theme=1
  • C++开发中,我们会经常用到插入操作对STL的各种容器进行操作,比如vector、map、set等。要知道,向 vector 容器中添加元素的唯一方式就是使用它的成员函数,如果不调用成员函数,非成员函数既不能添加也不能删除元素。这意味着,vector 容器对象必须通过它所允许的函数去访问,迭代器显然不行。
  • 可以用来给vector 容器添加元素的函数有 2 个,分别是 push_back() 和 emplace_back() 函数。
    有读者可能认为还有 insert() 和 emplace() 成员函数,严格意义上讲,这 2 个成员函数的功能是向容器中的指定位置插入元素。

1. push_back() :

  • 该成员函数的功能是在 vector 容器尾部添加一个元素,用法也非常简单,比如:
#include <iostream>
#include <vector>
using namespace std;
int main()
{
    vector<int> values{};
    values.push_back(1);
    values.push_back(2);
    for (int i = 0; i < values.size(); i++) {
        cout << values[i] << " ";
    }
    return 0;
}

运行程序,输出结果为:

1 2

2. emplace_back():

  • 该函数是 C++ 11 新增加的,其功能和 push_back() 相同,都是在 vector 容器的尾部添加一个元素。

  • 当调用push_back() 或insert() 成员函数时,是把元素类型的对象传递给它们,这些对象被拷贝到容器中。而当我们调用一个 emplace 系列函数时,则是将相应参数传递给元素类型的构造函数。这样emplace_back() 能就地通过参数构造对象,不需要拷贝操作,相比push_back() 能更好的避免内存的拷贝和移动,提升容器插入元素的性能。大多数情况都应该使用 emplace 系列函数:emplace; emplace_back; emplace_hit; emplace_fornt; emplace_after.

  • emplace_back() 成员函数的用法也很简单,这里直接举个例子:

#include <iostream>
#include <vector>
using namespace std;
int main()
{
    vector<int> values{};
    values.emplace_back(1);
    values.emplace_back(2);
    for (int i = 0; i < values.size(); i++) {
        cout << values[i] << " ";
    }
    return 0;
}

运行结果为:

1 2

思考:以上 2 段代码,只是用 emplace_back() 替换了 push_back(),既然它们实现的功能是一样的,那么 C++ 11 标准中为什么要多此一举呢?

3. emplace_back() 和 push_back() 的区别:

  • emplace_back() 和 push_back() 的区别,就在于底层实现的机制不同push_back() 向容器尾部添加元素时,首先会创建这个元素,然后再将这个元素拷贝或者移动到容器中(如果是拷贝的话,事后会自行销毁先前创建的这个元素);而 emplace_back() 在实现时,则是直接在容器尾部创建这个元素,省去了拷贝或移动元素的过程
  • 为了让大家清楚的了解它们之间的区别,我们创建一个包含类对象的 vector 容器,如下所示:
#include <vector> 
#include <iostream> 
using namespace std;
class testDemo
{
public:
    testDemo(int num):num(num){
        std::cout << "调用构造函数" << endl;
    }
    testDemo(const testDemo& other) :num(other.num) {
        std::cout << "调用拷贝构造函数" << endl;
    }
    testDemo(testDemo&& other) :num(other.num) {
        std::cout << "调用移动构造函数" << endl;
    }
private:
    int num;
};

int main()
{
    cout << "emplace_back:" << endl;
    std::vector<testDemo> demo1;
    demo1.emplace_back(2);  

    cout << "push_back:" << endl;
    std::vector<testDemo> demo2;
    demo2.push_back(2);
}

运行结果为:

emplace_back:
调用构造函数
push_back:
调用构造函数
调用移动构造函数

在此基础上,读者可尝试将 testDemo 类中的移动构造函数注释掉,再运行程序会发现,运行结果变为:

emplace_back:
调用构造函数
push_back:
调用构造函数
调用拷贝构造函数

由此可以看出,push_back() 在底层实现时,会优先选择调用移动构造函数,如果没有才会调用拷贝构造函数

  • 显然完成同样的操作,push_back() 的底层实现过程比 emplace_back() 更繁琐,换句话说,emplace_back() 的执行效率比 push_back() 高。因此,在实际使用时,建议大家优先选用 emplace_back()。

示例:

namespace {
    struct President {
        std::string name;
        std::string country;
        int year;
        
        President(std::string p_name, std::string p_country, int p_year)
            : name(std::move(p_name)), country(std::move(p_country)), year(p_year)
        {
            std::cout << "I am being constructed.\n";
        }
        President(President&& other)
            : name(std::move(other.name)), country(std::move(other.country)), year(other.year)
        {
            std::cout << "I am being moved.\n";
        }
        President& operator=(const President& other) = default;
    };
}

int emplacevspush()
{
    std::cout << "test_emplace_2()" << std::endl;
    /*
    The following code uses emplace_back to append an object of type President to a std::vector.
    It demonstrates how emplace_back forwards parameters to the President constructor and shows
    how using emplace_back avoids the extra copy or move operation required when using push_back.
    */
    std::vector<President> elections;
    std::cout << "emplace_back:\n";
    elections.emplace_back("Nelson Mandela", "South Africa", 1994);

    std::vector<President> reElections;
    std::cout << "\npush_back:\n";
    reElections.push_back(President("Franklin Delano Roosevelt", "the USA", 1936));

    std::cout << "\nContents:\n";
    for (President const& president : elections) {
        std::cout << president.name << " was elected president of "
            << president.country << " in " << president.year << ".\n";
    }
    for (President const& president : reElections) {
        std::cout << president.name << " was re-elected president of "
            << president.country << " in " << president.year << ".\n";
    }
    return 0;
}
//output
emplace_back:
I am being constructed.

push_back:
I am being constructed.
I am being moved.

Contents:
Nelson Mandela was elected president of South Africa in 1994.
Franklin Delano Roosevelt was re-elected president of the USA in 1936.

使用emplace_back() 函数可以减少一次拷贝或移动构造的过程,提升容器插入数据的效率。
由于 emplace_back() 是 C++ 11 标准新增加的,如果程序要兼顾之前的版本,还是应该使用 push_back()。

相关标签: c++