zoukankan      html  css  js  c++  java
  • c++11 之emplace_back 与 push_back的区别

      c++开发中我们会经常用到插入操作对stl的各种容器进行操作,比如vector,map,set等。在引入右值引用,转移构造函数,转移复制运算符之前,通常使用push_back()向容器中加入一个右值元素(临时对象)时,首先会调用构造函数构造这个临时对象,然后需要调用拷贝构造函数将这个临时对象放入容器中。原来的临时变量释放。这样造成的问题就是临时变量申请资源的浪费。 
    引入了右值引用,转移构造函数后,push_back()右值时就会调用构造函数和转移构造函数,如果可以在插入的时候直接构造,就只需要构造一次即可。这就是c++11 新加的emplace_back。

    emplace_back函数原型:

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

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

     1 #include <vector>  
     2 #include <string>  
     3 #include <iostream>  
     4  
     5 struct President  
     6 {  
     7     std::string name;  
     8     std::string country;  
     9     int year;  
    10  
    11     President(std::string p_name, std::string p_country, int p_year)  
    12         : name(std::move(p_name)), country(std::move(p_country)), year(p_year)  
    13     {  
    14         std::cout << "I am being constructed.
    ";  
    15     }
    16     President(const President& other)
    17         : name(std::move(other.name)), country(std::move(other.country)), year(other.year)
    18     {
    19         std::cout << "I am being copy constructed.
    ";
    20     }
    21     President(President&& other)  
    22         : name(std::move(other.name)), country(std::move(other.country)), year(other.year)  
    23     {  
    24         std::cout << "I am being moved.
    ";  
    25     }  
    26     President& operator=(const President& other);  
    27 };  
    28  
    29 int main()  
    30 {  
    31     std::vector<President> elections;  
    32     std::cout << "emplace_back:
    ";  
    33     elections.emplace_back("Nelson Mandela", "South Africa", 1994); //没有类的创建  
    34  
    35     std::vector<President> reElections;  
    36     std::cout << "
    push_back:
    ";  
    37     reElections.push_back(President("Franklin Delano Roosevelt", "the USA", 1936));  
    38  
    39     std::cout << "
    Contents:
    ";  
    40     for (President const& president: elections) {  
    41        std::cout << president.name << " was elected president of "  
    42             << president.country << " in " << president.year << ".
    ";  
    43     }  
    44     for (President const& president: reElections) {  
    45         std::cout << president.name << " was re-elected president of "  
    46             << president.country << " in " << president.year << ".
    ";  
    47     }
    48  
    49 }

    输出

    1 emplace_back:
    2 I am being constructed.
    3  
    4 push_back:
    5 I am being constructed.
    6 I am being moved.
    7  
    8 Contents:
    9 Nelson Mandela was elected president of South Africa in 1994.

      现在大多数情况下尽量使用emplace_back代替 push_back ,但是有没有什么特例是不能替换的呢,举一个例子:

    emplace_back造成的引用失效 

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

     1 #include <vector>
     2 #include <string>
     3 #include <iostream>
     4 using namespace std;
     5  
     6 int main()
     7 {
     8     vector<int> ivec;
     9     ivec.emplace_back(1);
    10     ivec.emplace_back(ivec.back());
    11     for (auto it = ivec.begin(); it != ivec.end(); ++it)
    12         cout << *it << " ";
    13     return 0;
    14 }
    15  
    16 //输出:
    17 1 -572662307 

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

     1 #include <vector>
     2 #include <string>
     3 #include <iostream>
     4 using namespace std;
     5  
     6 int main()
     7 {
     8     vector<int> ivec;
     9     ivec.emplace_back(1);
    10     auto &it = ivec.back();
    11     ivec.emplace_back(it);
    12     for (auto it = ivec.begin(); it != ivec.end(); ++it)
    13         cout << *it << " ";
    14     return 0;
    15 }
    16 输出:
    17 1 -572662307 

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

     1 #include <vector>
     2 #include <string>
     3 #include <iostream>
     4 using namespace std;
     5  
     6 int main()
     7 {
     8     vector<int> ivec;
     9     ivec.emplace_back(1);
    10     auto it = ivec.back();
    11     ivec.emplace_back(it);
    12     for (auto it = ivec.begin(); it != ivec.end(); ++it)
    13         cout << *it << " ";
    14     return 0;
    15 }
    16 输出:
    17 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引起重新分配内存:

     1 #include <vector>
     2 #include <string>
     3 #include <iostream>
     4 using namespace std;
     5  
     6 int main()
     7 {
     8     vector<int> ivec;
     9     ivec.reserve(4);
    10     ivec.emplace_back(1);
    11     ivec.emplace_back(ivec.back());
    12     for (auto it = ivec.begin(); it != ivec.end(); ++it)
    13         cout << *it << " ";
    14     return 0;
    15 }
    16 输出:
    17 1 1

    转载于:

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

    本文来自博客园,作者:Mr-xxx,转载请注明原文链接:https://www.cnblogs.com/MrLiuZF/p/14071320.html

  • 相关阅读:
    [NOI Online 提高组]冒泡排序
    三元上升子序列
    上帝造题的七分钟2 / 花神游历各国
    JZOJ 1038. 【SCOI2009】游戏
    android.view.View
    SQLServer导出导出单表数据
    Android-support-v4源码查看
    一些平台
    Installation error: INSTALL_FAILED_CPU_ABI_INCOMPATIBLE
    AsyncTask
  • 原文地址:https://www.cnblogs.com/MrLiuZF/p/14071320.html
Copyright © 2011-2022 走看看