欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

c++push_back 以及 emplace_back 的区别

程序员文章站 2022-03-21 16:33:42
...

区别

都说emplace快push慢,今天就详细研究下到底两者有什么区别,以及这个move在中间扮演了一个什么角色。
先上测试代码,是一个自己手写的str字符串类,几种构造函数,都写全了:

class str {
public:
    friend ostream& operator <<(ostream& out, const str& p);
    str() :data(nullptr), len(0) {}
    //构造函数
    str(const char* p) {
        len = strlen(p);
        data = new char[len + 1];
        strcpy(data, p);
        cout << "执行了char * 的构造函数" << endl;
    }
    //拷贝构造函数
    str(const str& p) {
        len = p.len;
        data = new char[len+1];
        strcpy(data, p.data);
        cout << "执行了 拷贝构造函数" << endl;
    }
    //重载赋值函数
    str& operator = (const str & p){
        if (this == &p) return *this;
        delete []data;
        len = p.len;
        data = new char[len + 1];
        strcpy(data, p.data);
        cout << "执行了拷贝赋值" << endl;
        return *this;
    }
    //移动构造函数
    str(str&& p) {
        if (this != &p) {
            len = p.len;
            data = p.data;
            p.data = nullptr;
            p.len = 0;
            cout << "执行了移动构造" << endl;
        }
    }
    ~str() {
        delete[]data;
        cout << "执行了析构" << endl;
    }
private:
    char * data;
    int len;
};
ostream& operator<<(ostream& out, const str& p) {
    out << p.data;
    return out;
}

下面开始分析:
先看看pushback做了什么

    vector<str> test;
    test.push_back("qqqqq");

结果显示:
执行了一次char *的构造函数又
执行了一次移动构造函数!
那么pushback的过程就很清楚了,在推入的是一组数据的时候,pushback会先调用构造函数,生成一个临时的变量(如果你有看我上一篇文章你就知道,这是一个右值!)生成完毕后,会在vector内部的连续存储区域开辟一块空间,然后把这个临时对象拷贝过去!
由于这是一个右值,我的str恰巧又写了移动构造函数,所以理所当然的调用了移动构造函数,效率还是快的,加入我没有写移动构造函数呢?那么就会调用拷贝构造函数。简而言之,pushback对于一组数据的输入就是两个过程,一次构造,一个拷贝构造(移动构造)!效率还是差的!

    str s1("ppppp");
    cout << "执行push_back" << endl;
    vector<str> test;
    test.push_back(s1);

这次显示只调用了一次拷贝构造,完成了一次拷贝而已,因为已经存在现成的对象了。

那咱们再来看看emplace_back:

    vector<str> test;
    test.emplace_back("1111111");

结果显示:只进行了一次构造函数。
那过程也很明白了,把数据拿过去,在原地构造了一边,对比push_back来说可就省了一次拷贝哦!

    vector<str> test;
    test.emplace_back(s1);

再来看看这种情况呢
结果显示:执行了一次拷贝构造!看来和push_back一样,本身这就是最优化的选择了!

本着刨根问底,这次向c++祖坟刨,再来看看move:

    str s1("ppppp");
    vector<str> test;
    test.emplace_back(move(s1));
    test.push_back(move(s1));
    test.push_back(move("ppppp"));

结果显示 前两个为移动构造
最后一个仍旧是 构造+拷贝构造

结果显而易见了,move和这两个其实是没什么关系的哦!很多人都被emplace接受的变量为右值引用给弄混了!

结论

1:move之决定了是调用拷贝构造,还是调用移动构造!
2:emplace使用原地构造的方式,但是如果给的是一
现成的对象,就会执行拷贝构造!效果和push一样!如果给的是原始数据,就执行原地构造效果就好了!
3:push_back如果给了原始数据,那么就执行构造+拷贝构造(有移动构造优先移动)如果给的是现成对象,那是纯拷贝构造了,和emplace没啥区别。
4:显而易见,emplace更加优越!尤其是在给定原始数据的时候!并且,当一个类不允许使用拷贝构造的时候,那么push一个原始的结果就是错的!只能用emplace原地构造这个数据!
ps:在实现一个线程池的时候,就有这样的错误!

相关标签: c++学习心得