变参模板 - 使得 emplace 可以接受任意参数,这样就可以适用于任意对象的构建
完美转发 - 使得接收下来的参数 能够原样的传递给对象的构造函数,这带来另一个方便性
这新特性主要是用来提升开发效率的!
下面举一个例子:
当一个容器存储一个对象时,编译器到底是如何运行的呢?需要怎么消耗性能?
我们需要定义一个类Human,实现构造函数,拷贝构造函数;私有数据成员age 和 name; 当把这样的类定义出的对象存入容器中,会发生怎么的操作呢?
这里以vector容器为例:
/*
C++11新特性 变参模板、完美转发和emplace
变参模板 - 使得 emplace 可以接受任意参数,这样就可以适用于任意对象的构建
完美转发 - 使得接收下来的参数 能够原样的传递给对象的构造函数,这带来另一个方便性
*/
#include <iostream>
#include <Windows.h>
#include <string>
#include <vector>
#include <deque>
#include <list>
using namespace std;
class Human {
public:
Human(int age, const string name) {
this->age = age;
this->name = name;
cout << "调用了有参数的构造函数!" << endl;
}
Human(const Human& human) {
this->age = human.age;
this->name = human.name;
cout << "调用了拷贝构造函数!" << endl;
}
void print(void) {
cout << "年龄:" << age << " 姓名:" << name << endl;
}
private:
int age;
string name;
};
int main(void) {
vector<Human> v1;
Human man(25, "男人");
// 第一种
v1.push_back(man);
v1[0].print();
// 第二种
v1.push_back(Human(26, "女人"));
v1[1].print();
system("pause");
return 0;
}
main函数中,我们把他分成了两种情况,
第一种是定义好对象后再存入容器中;
第二种是直接定义临时对象再存入容器中。
根据这两种情况我们运行一下:
怎么会调用了那么多次构造函数和拷贝构造函数呢?
下面我一行一行解释:
-
第一行:调用了有参构造函数
这是定义man对象是调用的!没有问题。
Human man(25, "男人");
-
第二行:调用了拷贝构造函数
这是将man存入容器中调用的,因为存入容器中并不是把对象的引用存进去,而是把对象的值存进去,所以需要调用拷贝构造函数;且容器里的对象与外面man对象毫无瓜葛!
v1.push_back(man);
-
第三行:打印结果!
-
第四行:调用了有参构造函数
这是创建临时对象时调用的!
v1.push_back(Human(26, "女人"));
里面的Human(26, "女人")
! -
第五行:调用了拷贝构造函数
- 这个千万不要以为是把临时对象存进容器中调用的,这里大有玄机。
- 当前v1容器中已经存有一个元素,他的内存长度也就只有一个元素那么大;
- 那么,内存不够,他又是如何能存储第二个对象进去呢?
- 原因是容器自动分配了一段新的内存,可用于存储两个对象的内存;
- 于是乎,就先把第一个对象的拷贝到新开辟的内存中,就调用拷贝构造函数!
如图:
-
第六行:调用了拷贝构造函数
内存分配好后,这个才是把第二个临时对象存入容器中所调用的拷贝构造函数!
v1.push_back(Human(26, "女人"));
如图:
-
第七行:打印结果!
至此,我已经详细的解释了每行语句的特性,相信大家也懂的了。
那么,调用那么多次构造函数,如此消耗性能,这对于 “高并发” 是最忌讳的。
所以,为了解决这样的问题,C++11 新标准推出了“变参模板、完美转发和emplace”可以将这个问题完美解决!
还是上面的代码,不过使用C++11新特性:
/*
C++11新特性 变参模板、完美转发和emplace
变参模板 - 使得 emplace 可以接受任意参数,这样就可以适用于任意对象的构建
完美转发 - 使得接收下来的参数 能够原样的传递给对象的构造函数,这带来另一个方便性
*/
#include <iostream>
#include <Windows.h>
#include <string>
#include <vector>
#include <deque>
#include <list>
using namespace std;
class Human {
public:
Human(int age, const string name) {
this->age = age;
this->name = name;
cout << "调用了有参数的构造函数!" << endl;
}
Human(const Human& human) {
this->age = human.age;
this->name = human.name;
cout << "调用了拷贝构造函数!" << endl;
}
void print(void) {
cout << "年龄:" << age << " 姓名:" << name << endl;
}
private:
int age;
string name;
};
int main(void) {
vector<Human> v1;
// C++ 11 新特性
v1.emplace_back(27, "外星人");
v1[0].print();
v1.emplace_back(28, "宇宙人");
v1[1].print();
system("pause");
return 0;
}
使用新特性再把对象存入容器中,可以直接使用临时对象,而不用再去创建一个对象,这样也大大增加了开发效率!
运行截图:
哇,我的天啊,居然少调用了两次拷贝构造函数,这里面的底层原理到底是如何实现的呢???
请看我一行一行为解释。
-
第一行:调用了有参构造函数
对应的是v1.emplace_back(27, "外星人");
他是创建对象时就把它存入容器中了,所以就不用再掉用拷贝构造函数。 -
第二行:打印结果
-
第三行:调用了有参构造函数
对应的是v1.emplace_back(28, "宇宙人");
这里又有玄机,他是先创建临时对象,但是并没有存入容器中,因为容器的内存也是不够存储第二个对象了,所以,得再重新分配一块内存。
然后再将临时对象存入容器的第二个位置中!!!
这点是小编的猜测,没有具体验证过!!!
如图:
-
第四行:调用了拷贝构造函数
这里是把容器原有的元素拷贝到新开辟的内存中
如图:
-
第五行:打印结果
到了这里,相信大家应该都知道了C++11的新特性的作用了吧!!!
deque 和 list 也是一样的,但是他们的执行效率会比vector更高。
请看代码示例:
/*
C++11新特性 变参模板、完美转发和emplace
变参模板 - 使得 emplace 可以接受任意参数,这样就可以适用于任意对象的构建
完美转发 - 使得接收下来的参数 能够原样的传递给对象的构造函数,这带来另一个方便性
*/
#include <iostream>
#include <Windows.h>
#include <string>
#include <vector>
#include <deque>
#include <list>
using namespace std;
class Human {
public:
Human(int age, const string name) {
this->age = age;
this->name = name;
cout << "调用了有参数的构造函数!" << endl;
}
Human(const Human& human) {
this->age = human.age;
this->name = human.name;
cout << "调用了拷贝构造函数!" << endl;
}
void print(void) {
cout << "年龄:" << age << " 姓名:" << name << endl;
}
private:
int age;
string name;
};
int main(void) {
vector<Human> v1;
cout << "---deque---" << endl;
deque<Human> d1;
d1.emplace_back(25, "男人");
d1[0].print();
d1.emplace_back(26, "女人");
d1[1].print();
cout << endl << endl << "---list---" << endl;
list<Human> list;
list.emplace_back(25, "男人");
list.emplace_front(26, "女人");
system("pause");
return 0;
}
运行截图:
看到了吧,他们的执行效率确实是比vector要高!!!
不懂vector容器的可以点这里;
不懂deque容器的可以点这里;
不懂list容器的可以点这里。
另外deque 和 list 容器都比vector容器多一种用法:
在第一个元素位置插入:
d1.emplace_front(21"男人");
list.emplace_front(20"女人");
此外, 他们三个共有的一个除了emplace_back外,还有一个emplace;他相当于insert插入函数。
例:
在list容器最后的位置插入
list.emplace(list.end(), 18, "灭霸"); // 相当于insert
下面是全部测试代码:
/*
C++11新特性 变参模板、完美转发和emplace
变参模板 - 使得 emplace 可以接受任意参数,这样就可以适用于任意对象的构建
完美转发 - 使得接收下来的参数 能够原样的传递给对象的构造函数,这带来另一个方便性
*/
#include <iostream>
#include <Windows.h>
#include <string>
#include <vector>
#include <deque>
#include <list>
using namespace std;
class Human {
public:
Human(int age, const string name) {
this->age = age;
this->name = name;
cout << "调用了有参数的构造函数!" << endl;
}
Human(const Human& human) {
this->age = human.age;
this->name = human.name;
cout << "调用了拷贝构造函数!" << endl;
}
void print(void) {
cout << "年龄:" << age << " 姓名:" << name << endl;
}
private:
int age;
string name;
};
int main(void) {
vector<Human> v1;
//Human man(25, "男人");
//// 第一种
//v1.push_back(man);
//v1[0].print();
//// 第二种
//v1.push_back(Human(26, "女人"));
//v1[1].print();
// C++ 11 新特性
/*v1.emplace_back(27, "外星人");
v1[0].print();
v1.emplace_back(28, "宇宙人");
v1[1].print();*/
cout << "---deque---" << endl;
deque<Human> d1;
d1.emplace_back(25, "男人");
d1[0].print();
d1.emplace_back(26, "女人");
d1[1].print();
cout << endl << endl << "---list---" << endl;
list<Human> list;
list.emplace_back(25, "男人");
list.emplace_front(26, "女人");
//list.emplace(list.end(), 18, "灭霸"); // 相当于insert
system("pause");
return 0;
}
总结:
变参模板 - 使得 emplace 可以接受任意参数,这样就可以适用于任意对象的构建
完美转发 - 使得接收下来的参数 能够原样的传递给对象的构造函数,这带来另一个方便性
博主还是一个C++萌新,如果博文里面有哪些地方是解析错误的,希望大神们能指点出来,谢谢!