谈谈c++11 emplace_back

本文详细对比了C++11中vector容器的emplace_back()和push_back()两种添加元素的方法。emplace_back()通过原地构造避免了额外的对象拷贝,提高了效率,尤其在处理复杂对象时更为明显。同时,文章指出emplace_back()更适合接收右值,而push_back()则能处理左右值,但对左值的处理效率较低。总结了使用这两个函数时应注意的场景和优化策略。
摘要由CSDN通过智能技术生成


前言

vector 、string、list等容器提供的所有成员函数,在这些成员函数中,可以用来给容器中添加元素的函数有 2 个,分别是 push_back()emplace_back() 函数。


一、emplace_back()用法

功能:和 push_back() 相同,都是在 vector 容器的尾部添加一个元素。

emplace_back函数原型:

template <class... Args>  
void emplace_back (Args&&... args);

举例:

#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

二、为什么要使用emplace_back()

即:与push_back()对比有什么可取之处

emplace_back()和push_back()的区别

场景一:考虑是否原地构造

push_back():向容器中加入一个右值元素(临时对象)时,首先会调用构造函数构造这个临时对象,然后需要调用拷贝构造函数将这个临时对象放入容器中。原来的临时变量释放。这样造成的问题就是临时变量申请资源的浪费。

emplace_back():引入了右值引用,转移构造函数,在插入的时候直接构造,只需要构造一次即可。

也就是说,两者的底层实现的机制不同。push_back() 向容器尾部添加元素时,首先会创建这个元素,然后再将这个元素拷贝或者移动到容器中(如果是拷贝的话,事后会自行销毁先前创建的这个元素);而 emplace_back() 在实现时,则是直接在容器尾部创建这个元素,省去了拷贝或移动元素的过程。

代码如下(示例):

#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:
调用构造函数
调用拷贝构造函数

结果分析:emplace_back 接收参数时,可以调用匹配的构造函数实现在容器内的原地构造

由此可以看出,push_back() 在底层实现时,会优先选择调用移动构造函数,如果没有的话,则直接在容器的末尾构造对象,这样就省去了拷贝的过程。

显然完成同样的操作,push_back() 的底层实现过程比 emplace_back() 更繁琐,换句话说,emplace_back() 的执行效率比 push_back() 高。

场景二:考虑尾插左值和右值

代码如下(示例):

#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()
{  
    testDemo t1(2);
    cout << "emplace_back:" << endl;
    std::vector<testDemo> demo1;
    demo1.emplace_back(t1);  
    //demo1.emplace_back(move(t1));  

    cout << "push_back:" << endl;
    std::vector<testDemo> demo2;
    demo2.push_back(t1);
    //demo1.push_back(move(t1));  
}

输出结果为:

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

去掉代码中注释部分,并注释掉容器接收t1的语句,即让push_back()和emplace_back() 接收右值。
输出结果如下:

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

从执行结果中,我们可以得出以下结论

  1. push_back 可以接收左值也可以接受右值,接收左值时使用拷贝构造,接收右值时使用移动构造
  2. emplace_back 接收右值时调用类的移动构造
  3. emplace_back 接收左值时,实际上的执行效果是先对传入的参数进行拷贝构造,然后使用拷贝构造后的副本,也就是说,emplace_back在接收一个左值的时候其效果和push_back一致!所以在使用emplace_back 时需要确保传入的参数是一个右值引用,如果不是,请使用std::move()进行转换

notice:

本文摘自(尊重原创):
c++11 之emplace_back 与 push_back的区别
c++11 右值引用,移动构造函数,emplace_back 解析

  • 76
    点赞
  • 362
    收藏
    觉得还不错? 一键收藏
  • 10
    评论
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值