又被拷打了捏,问起来还是不会细节…
vector 的 push_back() 和 emplace_back()
我们来看一些代码:
vec是vector
1.传递对象实例
2.传递临时对象
3.直接传递数据成员(主要要按顺序)
所以总结:
当%_back()的是 类对象实例 或者 临时对象 的时候,两者没有区别;
当 直接传递数据成员 的时候就不一样了:
push_back():先调用第二种构造,构造出临时变量,接着调用 移动构造/拷贝构造 函数,在vector的内存上面构造。
Emplace_back():直接调用第二种构造在vector的内存上面构造。
那么具体是怎么构造的呢?为什么会这样?
看源码:(说实话,还没没看懂是怎么构造的…)
关键就是 Emplace_back()用了 完美转发 + 可变模板参数 的技术;
Push_back() 底层用的就是emplace_back(),但是它只能接受对象引用以及右值引用,而不能直接接受 成员数据 作为参数:
因此push_back()无法接受 成员数据 作为参数;
而对于 emplace_back() 就不一样了,emplace_back()用了一个 可变模板参数 + 完美转发的技术:
然后通过不断的 forward完美转发,分为下面的几种情况:
1. 传进来的是左值: 那么args就是一个左值,一直转发,最后调用拷贝构造函数,所以push_back(),emplace_back()是一样的;
2. 传进来的是右值: 那么args就是一个右值,一直转发,最后调用移动构造函数/拷贝构造函数,所以两者还是一样;
3. 传进来的是类的成员数据作为参数: 那么args就是一堆参数了,可能不只是一个了,那么一直转发,最终将调用我们实现的函数
在vector末尾直接构造。
函数返回值
接着来看个有意思的现象:(构造函数们还是上面的构造函数)
非常的合理,函数返回值是右值,调用移动构造函数;
段错误了…
但是我们把构造函数里面注释了,就不会段错误了…
非常合理,函数返回引用,引用是左值,所以调用拷贝构造函数;
那么问题来了,同样是返回引用,为什么第二个会段错误呢?
本质就是对函数返回值和内存管理没有正确的认识…
首先我们要知道一个知识点:
对于 堆、栈、全局区 中的变量的生命周期是不同的!
对于栈中的变量,也就是我们直接定义的变量(不通过malloc,new;非静态),其生命周期是在它的作用域里面,离开了作用域则自动销毁了!
对于堆中的变量,也就是我们malloc,new的变量,即使其离开了作用域,但是生命周期仍存在,所以需要我们手动delete掉;
对于全局区中的变量,它的作用域的整个程序,所以程序结束后才销毁;
回到上面的问题:
由于 fun1 返回的局部变量的值,即使是局部变量(在栈上,离开函数后销毁),但是返回的是值,也就是会产生一个临时变量,返回的是临时变量,通过临时变量构造,没有问题。
由于fun2返回的是局部变量的引用,而局部变量在栈上,离开函数后销毁,并且并且由于返回值是引用,所以并不会产生临时变量,该引用是函数中a的引用,而函数结束后a销毁了,引用就变成了未定的,所以在拷贝构造函数中使用a._x的时候就出错了,因为a的内存空间已经被销毁了;这也是为什么注释掉后就不会了;
再看fun3,同样返回引用,但是注意到函数中的变量是new出来的,在堆上(离开函数后并不会销毁),因此返回的引用是有效的!因此不会报错;
有意思的是,返回字符串也没问题:
因为首先返回的是指针,且不是new出来的,但是hello是常量,放在常量区(属于全局区),所以函数结束后不会被销毁。(同理static也是一样的)
最后一句话的意思是:函数 传出参数 的意思。
参考文章:
C++:vector的push_back()与emplace_back()
C++vector等容器使用push_back和emplace_back的区别
vector中emplace_back和push_back详解,源码解读
我是一个找实习的鼠鼠,今天又是 0 offer 的一天,加油吧!