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