文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

一篇文章搞懂 push_back 和 emplace_back 的区别

2023-08-20 16:33

关注

push_back 和 emplace_back

本来以为自己对 push_back 和 emplace_back 的理解还行,直到我室友伦伦问了一个关于 push_back 和 emplace_back 的问题。死去的 modern effective c++ 记忆又开始攻击我…因此,我痛定思痛,在阅读了大量文献之后写下本文。

(全篇 1200 字阅读大约需要 8 分钟)

撰写本文出于两点原因:

  1. 该问题在面试频频出现
  2. 网上众说纷纭,缺乏系统全面的解释

什么是 push_back?什么是 emplace_back?

push_back

我们在使用 STL 容器时,以 std::vector 为例,经常会用到 push_back 向数组尾部添加一个新的元素。push_back 的作用:向容器尾部添加一个新的元素。

如果我们熟悉 c++ 模板编程,并且对底层实现有兴趣不妨看看它的定义:(没兴趣可以直接跳过)

_CONSTEXPR20 void push_back(const _Ty& _Val) { // insert element at end, provide strong guarantee    _Emplace_one_at_back(_Val);}_CONSTEXPR20 void push_back(_Ty&& _Val) {    // insert by moving into element at end, provide strong guarantee    _Emplace_one_at_back(_STD move(_Val));}

什么是 _Ty:在使用模板容器时传入到模板中的类型,比如 vector 那么 _Ty 将会是 int。

emplace_back

emplace_back 在功能上和 push_back 没有区别,在使用上你完全可以放心地把 push_back 替换成 emplace_back(不可逆)。

emplace_back 的定义稍微复杂一些:

template <class... _Valty>_CONSTEXPR20 decltype(auto) emplace_back(_Valty&&... _Val) {    _Ty& _Result = _Emplace_one_at_back(_STD forward<_Valty>(_Val)...);    return _Result;}

为什么需要 emplace_back?

这个时候有人就要问了,明明 push_back 就可以实现功能了为什么要整出一个 emplace_back? 不妨一起看看下面这个例子:

int main() {    std::vector<std::string> sentences;    sentences.push_back("hello, world");    return 0;}// compile success!

上述代码通过编译,一切看起来万事大吉,但是存在一个隐患:从源码上可以看到 push_back 只接受 _Ty 类型(也就是 std::string),但是 “hello, world” 是 char [13] 类型,显然不满足条件,为啥可以编译通过,这是因为类的隐式转换(见附录)。因此上述代码实际上等价于:

int main() {    std::vector<std::string> sentences;    sentences.push_back(std::string("hello, world"));    return 0;}

可以看到,上述代码其实在 main 函数里构造了一个临时的 std::string 对象,这是我们不想要看到的(试想某个对象的构造非常消耗时间)。如果利用 emplace_back 就可以完美解决这个问题:

int main() {    std::vector<std::string> sentences;    sentences.emplace_back("hello, world");    return 0;}

这时我们不再需要隐式转换,因为 emplace_back 接受可变参数,因此直接传入到 emplace_back 函数中进行构造(接下来的操作和 push_back 如出一辙)。

最后看一个综合的例子:

class Person{    string name;public:    Person(const char *p)    {        cout << "construct" << endl;    }    Person(const Person &p)    {        cout << "Person(const Person&)" << endl;    }};int main(){    std::vector<Person> persons;    persons.reserve(2);    cout << "---------emplace back---------" << endl;    persons.emplace_back("John");    cout << "---------push back---------" << endl;    persons.push_back("mike");    return 0;}// Output:// ---------emplace back---------// construct// ---------push back---------// construct// Person(const Person&)

可以看到 emplace_back 只会调用一次构造函数,不会在原地构造完之后再次调用复制构造函数。

emplace_back 的优势

后记

尽情地把 push_back 替换为 emplace_back 吧~

虽然 emplace_back 仿佛明显优于 push_back,但是这并不意味这 emplace 就一定优于 insert,因为具体的对象类型,和容器内部的组织形式决定了其有效性。因此,在使用过程中我们还需对 emplace 替换 insert 多加考虑。(可以参考 《modern effective c++》)

Appendix

类的隐式转换:某个类存在单个参数的构造函数,那么我们可以将该类型的参数隐式转换为一个该类对象。比如:

class Person{    string name;public:    Person(const char *p) {        for (int i = 0; i < strlen(p); ++i)        {            name.push_back(p[i]);        }    }    string getName() {        return name;    }};int main() {    Person mike = "mike";    cout << mike.getName() << endl;    return 0;}// output:// mike

如果我们不想要这种隐式转换出现,可以用 explicit 来修饰构造函数:

class Person{    string name;public:    explicit Person(char *p) {        for (int i = 0; i < strlen(p); ++i)        {            name.push_back(p[i]);        }    }    string getName() {        return name;    }};int main() {    Person mike = "mike";    cout << mike.getName() << endl;    return 0;}// compile error// vector_push_back_test.cpp:62:19: error:conversion from ‘const char [5]’ to non-scalar type ‘Person’ requested

来源地址:https://blog.csdn.net/qq_37124218/article/details/127016701

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯