C++11 新特性 之 变参模板、完美转发和emplace

变参模板 - 使得 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函数中,我们把他分成了两种情况,
第一种是定义好对象后再存入容器中;
第二种是直接定义临时对象再存入容器中。

根据这两种情况我们运行一下:
在这里插入图片描述
怎么会调用了那么多次构造函数和拷贝构造函数呢?
下面我一行一行解释:

  1. 第一行:调用了有参构造函数
    这是定义man对象是调用的!没有问题。
    Human man(25, "男人");

  2. 第二行:调用了拷贝构造函数
    这是将man存入容器中调用的,因为存入容器中并不是把对象的引用存进去,而是把对象的值存进去,所以需要调用拷贝构造函数;且容器里的对象与外面man对象毫无瓜葛!
    v1.push_back(man);

  3. 第三行:打印结果!

  4. 第四行:调用了有参构造函数
    这是创建临时对象时调用的!
    v1.push_back(Human(26, "女人"));里面的Human(26, "女人")!

  5. 第五行:调用了拷贝构造函数

  1. 这个千万不要以为是把临时对象存进容器中调用的,这里大有玄机。
  2. 当前v1容器中已经存有一个元素,他的内存长度也就只有一个元素那么大;
  3. 那么,内存不够,他又是如何能存储第二个对象进去呢?
  4. 原因是容器自动分配了一段新的内存,可用于存储两个对象的内存;
  5. 于是乎,就先把第一个对象的拷贝到新开辟的内存中,就调用拷贝构造函数!
    如图:
    在这里插入图片描述
  1. 第六行:调用了拷贝构造函数
    内存分配好后,这个才是把第二个临时对象存入容器中所调用的拷贝构造函数!
    v1.push_back(Human(26, "女人"));
    如图:
    在这里插入图片描述

  2. 第七行:打印结果!


至此,我已经详细的解释了每行语句的特性,相信大家也懂的了。

那么,调用那么多次构造函数,如此消耗性能,这对于 “高并发” 是最忌讳的。
所以,为了解决这样的问题,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;
}

使用新特性再把对象存入容器中,可以直接使用临时对象,而不用再去创建一个对象,这样也大大增加了开发效率!

运行截图:
在这里插入图片描述

哇,我的天啊,居然少调用了两次拷贝构造函数,这里面的底层原理到底是如何实现的呢???

请看我一行一行为解释。

  1. 第一行:调用了有参构造函数
    对应的是v1.emplace_back(27, "外星人");
    他是创建对象时就把它存入容器中了,所以就不用再掉用拷贝构造函数。

  2. 第二行:打印结果

  3. 第三行:调用了有参构造函数
    对应的是v1.emplace_back(28, "宇宙人");
    这里又有玄机,他是先创建临时对象,但是并没有存入容器中,因为容器的内存也是不够存储第二个对象了,所以,得再重新分配一块内存。
    然后再将临时对象存入容器的第二个位置中!!!

这点是小编的猜测,没有具体验证过!!!

如图:
在这里插入图片描述

  1. 第四行:调用了拷贝构造函数
    这里是把容器原有的元素拷贝到新开辟的内存中
    如图:
    在这里插入图片描述

  2. 第五行:打印结果


到了这里,相信大家应该都知道了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++萌新,如果博文里面有哪些地方是解析错误的,希望大神们能指点出来,谢谢!

发布了39 篇原创文章 · 获赞 17 · 访问量 3819

猜你喜欢

转载自blog.csdn.net/cpp_learner/article/details/104678677
今日推荐