一、emplace_back与push_back原理区别

当我们传递一个对象的参数给这两个函数时,分别由如下区别:
emplace_back支持直接将构造函数所需的参数传递过去,然后构建一个新的对象出来,然后填充到容器尾部的。
push_back首先会利用传入的参数调用构造函数构造这个临时对象,然后需要调用拷贝构造函数将这个临时对象放入容器中。原来的临时变量释放。这样会造成效率低下。

代码如下(示例):

#include <vector>
#include <string>
#include <iostream>
using namespace std;

class test
{
 public:
    test(int num): num(num){
        cout << "i am default construct!" << endl;
    }
    test(const test& t) {
        cout << "i am copy construct!" << endl;
    }
    ~test(){
        cout << "i am ~ construct!" << endl;
    }
    int num;
};
 
int main()
{
    vector<test> ivec;
    ivec.reserve(4);
    cout << endl << "ivec.push_back(1): ";
    ivec.push_back(1);
    cout << endl << "ivec.emplace_back(1): ";
    ivec.emplace_back(2);
    cout << endl << "end!!!!!!" << endl;
    return 0;
}

上面的输出结果:
在这里插入图片描述
可见,确实是push_back,会有:调用默认构造函数构建临时变量—》调用拷贝构造函数—》调用析构函数;
而emplace_back只有一个默认构造过程。

二、push构造时不支持无参数情况,emplace支持

代码如下(示例):

#include <vector>
#include <string>
#include <iostream>
using namespace std;

class test
{
 public:
     test(): num(0){
        cout << "i am default construct!" << endl;
    }
    test(int num): num(num){
        cout << "i am default construct!" << endl;
    }
    test(const test& t) {
        cout << "i am copy construct!" << endl;
    }
    test(const test&& t): num(t.num) {
        cout << "i am move construct!" << endl;
    }
    ~test(){
        cout << "i am ~ construct!" << endl;
    }
    int num;
};

test gettest() {
    test t(11000);
    return t;
}
 
int main()
{
    vector<test> ivec;

    cout << endl << "ivec.emplace_back(): ";
    ivec.emplace_back();
    // 如果编译通过时,这里输出0。c++11里基本类型变量没有初始化时,默认初始化为0
    cout << ivec.back().num << endl;
    // 到这一步编译报错 
    cout << endl << "ivec.emplace_back(): ";
    ivec.push_back();

    cout << endl << "end!!!!!!" << endl;

    return 0;
}

编译报错:
在这里插入图片描述

三、move语义

move语义的用法陆陆续续看了好多文章,还是一知半解T…T,等以后再好好学习,这里主要是emplace也用到了move的东西。可以参考下文章:https://blog.csdn.net/p942005405/article/details/84644069
写个代码记录下。如下:

#include <vector>
#include <string>
#include <iostream>
using namespace std;

class test
{
 public:
     test(): num(0){
        cout << "i am default construct!" << endl;
    }
    test(int num): num(num){
        cout << "i am default construct!" << endl;
    }
    test(const test& t) {
        cout << "i am copy construct!" << endl;
    }
    test(const test&& t): num(t.num) {
        cout << "i am move construct!" << endl;
    }
    ~test(){
        cout << "i am ~ construct!" << endl;
    }
    int num;
};

test gettest() {
    test t(11000);
    return t;
}
 
int main()
{
    vector<test> ivec;
    ivec.reserve(8);

    cout << endl << "ivec.emplace_back(gettest()): ";
    // 或者ivec.emplace_back(test(3)); 一样达到返回一个临时变量的效果,临时变量是右值
    ivec.emplace_back(gettest());
    cout << ivec.back().num << endl;

    test t(4);
    cout << endl << "ivec.emplace_back(t): ";
    ivec.emplace_back(t);

    cout << endl << "ivec.emplace_back(move(t)): ";
    ivec.emplace_back(move(t));

    cout << t.num << endl;

    cout << endl << "end!!!!!!" << endl;

    return 0;
}

在这里插入图片描述
move语义待续。。。

四、 vector内存开辟原理

为了避免vector空间不够开辟新内存进行元素拷贝是的干扰,添加语句 ivec.reserve(4) 是有必要的。
在开始代码测试的时候,一直会出现多了一个拷贝构造函数的过程,原来是vector在没有初始化时,容量为0,当push元素进去但内存不够时,vector按照当前两倍的大小开辟新的内存块,因此就需要将就内存块的元素拷贝到新的内存块,同时销毁旧内存上的元素。见如下示例:

#include <vector>
#include <string>
#include <iostream>
using namespace std;

class test
{
 public:
    test(int num): num(num){
        cout << "i am default construct!" << endl;
    }
    test(const test& t) {
        cout << "i am copy construct!" << endl;
    }
    ~test(){
        cout << "i am ~ construct!" << endl;
    }
    int num;
};
 
int main()
{
    vector<test> ivec;
    // ivec.reserve(4);
    cout << "capacity:" << ivec.capacity() << endl;
    cout << endl << "ivec.push_back(1): ";
    ivec.push_back(1);
    cout << "capacity:" << ivec.capacity() << endl;
    cout << endl << "ivec.emplace_back(1): ";
    ivec.emplace_back(2);
    cout << endl << "ivec.emplace_back(1): ";
    ivec.emplace_back(2);
    cout << "capacity:" << ivec.capacity() << endl;
    cout << endl << "end!!!!!!" << endl;

    return 0;
}

输出结果:
在这里插入图片描述
可见,每次当vector预留的内存空间不够,开辟内存后,对之前的每个元素(不包含当前元素)生成一份拷贝,再把原来的析构,当元素量大的时候,会造成相当大的效率问题!!!!


参考文章:
https://zhuanlan.zhihu.com/p/213853588
https://blog.csdn.net/ShenHang_/article/details/120267857
https://blog.csdn.net/p942005405/article/details/84764104
https://blog.csdn.net/p942005405/article/details/84644069

PS: 这算是自己第一次认真且相对完整的写一篇博客,直观感觉就是:通过查询资料和自己编程实现与验证自己的想法时,很有收获但真不容易,这文章前前后后花了肯定有六七个小时!!!!纪念一下,希望以后保持习惯和加快速度!!!!

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐