c++push_back 以及 emplace_back 的区别

Oprah ·
更新时间:2024-09-21
· 824 次阅读

区别

都说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 test; test.push_back("qqqqq");

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

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

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

那咱们再来看看emplace_back:

vector test; test.emplace_back("1111111");

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

vector test; test.emplace_back(s1);

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

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

str s1("ppppp"); vector 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:在实现一个线程池的时候,就有这样的错误!


作者:你才喜欢菜虚鲲



back c+

需要 登录 后方可回复, 如果你还没有账号请 注册新账号