【C++11】emplace_back 与 push_back的区别

70 篇文章 18 订阅

原文:https://blog.csdn.net/p942005405/article/details/84764104

C++容器比如vector,map,set等的插入操作,通常使用push_back()向容器中加入一个右值元素(临时对象,没有地址,临时存在寄存器中)时,首先会调用构造函数构造这个临时对象,然后需要调用拷贝构造函数将这个临时对象放入容器中,然后在析构这个临时变量(释放)。这里的问题是临时变量申请资源然后又释放,这两个操作浪费时间和算力。 

引入了右值引用,转移构造函数后,push_back()右值时就会调用构造函数和转移构造函数,如果可以在插入的时候直接构造,就只需要构造一次即可。这就是c++11 新加的emplace_back。

emplace_back函数原型:

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

在容器尾部添加一个元素,这个元素原地构造,不需要触发拷贝构造和转移构造。而且调用形式更加简洁,直接根据参数初始化临时对象的成员。
一个很有用的例子:

#include <vector>  
#include <string>  
#include <iostream>  
 
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(const President& other)
        : name(std::move(other.name)), country(std::move(other.country)), year(other.year)
    {
        std::cout << "I am being copy 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);  
};  
 
int main()  
{  
    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";  
    }
 
}

输出

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.

网上有人说尽量使用emplace_back代替 push_back 有没有什么特例是不能替换的呢,搜了一下发现了一个例子:

emplace_back造成的引用失效 

勘误:window visual studio 2015 编译下面程序会出现 引用失效问题,而linux gcc 和qt 等编译环境中未出现下面问题。感谢大家的指正。

#include <vector>
#include <string>
#include <iostream>
using namespace std;
 
int main()
{
    vector<int> ivec;
    ivec.emplace_back(1);
    ivec.emplace_back(ivec.back());
    for (auto it = ivec.begin(); it != ivec.end(); ++it)
        cout << *it << " ";
    return 0;
}
 
//输出:
1 -572662307 

尝试1:不直接给emplace_back传递ivec.back():

#include <vector>
#include <string>
#include <iostream>
using namespace std;
 
int main()
{
    vector<int> ivec;
    ivec.emplace_back(1);
    auto &it = ivec.back();
    ivec.emplace_back(it);
    for (auto it = ivec.begin(); it != ivec.end(); ++it)
        cout << *it << " ";
    return 0;
}
输出:
1 -572662307 

尝试2:不给emplace_back传递引用:

#include <vector>
#include <string>
#include <iostream>
using namespace std;
 
int main()
{
    vector<int> ivec;
    ivec.emplace_back(1);
    auto it = ivec.back();
    ivec.emplace_back(it);
    for (auto it = ivec.begin(); it != ivec.end(); ++it)
        cout << *it << " ";
    return 0;
}
输出:
1 1

我们如愿以偿,这时候应该可以得到结论了,ivec.back()返回的是引用,但是这个引用失效了,所以才会输出不正确;我们之前也提到过,重新分配内存会造成迭代器的失效,这里是造成了引用的失效。

再回头看看emplace_back的描述: 
if a reallocation happens, all iterators, pointers and references related to this container are invalidated. 
Otherwise, only the end iterator is invalidated, and all other iterators, pointers and references to elements are guaranteed to keep referring to the same elements they were referring to before the call.

进一步
尝试3:避免emplace_back引起重新分配内存:

#include <vector>
#include <string>
#include <iostream>
using namespace std;
 
int main()
{
    vector<int> ivec;
    ivec.reserve(4);
    ivec.emplace_back(1);
    ivec.emplace_back(ivec.back());
    for (auto it = ivec.begin(); it != ivec.end(); ++it)
        cout << *it << " ";
    return 0;
}
输出:
1 1

参考链接:

https://blog.csdn.net/windpenguin/article/details/75581552 

https://blog.csdn.net/xiaolewennofollow/article/details/52559364 

https://blog.csdn.net/wangshubo1989/article/details/50358044 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值