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

参考:

  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()。

  • 19
    点赞
  • 47
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值