【C++】新的类功能和可变参数模板

一、新的类功能

1.1 默认成员函数

在之前的学习过程中,我们已经知道了6个默认成员函数,分别是:

  1. 构造函数
  2. 析构函数
  3. 拷贝构造
  4. 拷贝赋值重载
  5. 取地址重载
  6. const 取地址重载

默认成员函数的特点:我们不写,编译器会生成出来一个默认的

C++11新增了两个:

  1. 移动构造函数
  2. 移动赋值运算符重载

1.1.1 移动构造函数

默认移动构造函数出现的条件:

  • 自己没有实现移动构造函数
  • 并且自己没有实现析构、拷贝构造和拷贝赋值重载函数中的任意一个
  • 默认生成的移动构造函数,对于内置类型成员会执行逐成员按字节拷贝,自定义类型成员,则需要看这个成员是否实现移动构造,如果实现了就调用移动构造,没有实现就调用拷贝构造
class Person
{
public:
	Person(const char* name = "", int age = 0)
		:_name(name)
		, _age(age)
	{}
	
	/*~Person()
	{}*/
private:
	yss::string _name;
	int _age;
};
int main()
{
	Person s1;
	Person s2 = std::move(s1);//右值
	return 0;
}

在这里插入图片描述

取消注释后运行:

	~Person()
	{}

在这里插入图片描述

1.1.2 移动赋值运算符重载

默认移动赋值运算符重载出现的条件:

  • 自己没有实现移动赋值运算符重载
  • 并且自己没有实现析构、拷贝构造和拷贝赋值重载函数中的任意一个
  • 默认生成的移动赋值运算符重载函数,对于内置类型成员会执行逐成员按字节拷贝,自定义类型成员,则需要看这个成员是否实现移动赋值重载,如果实现了就调用移动赋值重载函数,没有实现就调用拷贝赋值
class Person
{
public:
	Person(const char* name = "", int age = 0)
		:_name(name)
		, _age(age)
	{}
	
	//~Person()
	//{}

private:
	yss::string _name;
	int _age;
};
int main()
{
	Person s1;
	Person s2;
	s2 = std::move(s1);
	return 0;
}

在这里插入图片描述

取消注释后运行:

	~Person()
	{}

在这里插入图片描述

1.2 关键字default

作用:强制生成默认函数。 如果我们有写构造函数,就不会生成移动构造,可以使用default关键字显示指定移动构造生成。

class Person
{
public:
	Person(const char* name = "", int age = 0)
		:_name(name)
		, _age(age)
	{}
	//拷贝构造
	Person(const Person& p)
		:_name(p._name)
		, _age(p._age)
	{}
	//移动构造
	Person(Person&& p) = default;
private:
	yss::string _name;
	int _age;
};

在这里插入图片描述

1.3 关键字delete

作用:禁止生成默认函数。 只需在该函数声明加上=delete即可,该语法指示编译器不生成对应函数的默认版本,称=delete修饰的函数为删除函数。

在这里插入图片描述

二、可变参数模板

2.1 可变参数的函数模板

以前学习的是类模板和函数模板,这两个的特点是只能含固定数量的模版参数。可变参数模板在此基础上有了很大的提升,可以接收0到N个模板参数

下面就是一个基本可变参数的函数模板:

template <class ...Args>
void ShowList(Args... args)
{}
  • Args是一个模板参数包,args是一个函数形参参数包
  • 声明一个参数包Args…args,这个参数包中可以包含0到任意个模板参数

2.2 递归方式展开函数

先看看显示函数参数包中的参数个数:

template <class ...Args>
void ShowList(Args... args)
{
	cout << sizeof...(args) << endl;
}

int main()
{
	ShowList();//0
	ShowList(1);//1
	ShowList(1, 'A');//2
	ShowList(1, 'A', std::string("sort"));//3
	return 0;
}

如果想打印函数参数包中的每个参数呢,使用for循环?
在这里插入图片描述
很明显是不行的,因为模板是编译时解析,for循环是运行时解析参数。

方法:编译时递归解析

//递归终止条件
void _ShowList()
{
	cout << endl;
}
//子函数中递归
template <class T, class ...Args>
void _ShowList(const T& val, Args... args)
{
	cout << val << " ";
	_ShowList(args...);
}
//传入函数参数包
template <class ...Args>
void ShowList(Args... args)
{
	_ShowList(args...);
}

int main()
{
	ShowList(1);
	ShowList(1, 'A');
	ShowList(1, 'A', std::string("sort"));
	return 0;
}

在这里插入图片描述

分析:以参数为3个为例
在这里插入图片描述

2.3 empalce

empalce系列中的emplace_back函数与push_back函数的功能是相同的,都是尾插数据。但是C++11新增empalce函数系列支持模板的可变参数和万能引用。下面的是emplace_back函数:
在这里插入图片描述

通过以下代码的对比,区分出empalce函数的优势在哪:
1️⃣深拷贝类对象

int main()
{
	list<yss::string> lt1;
	yss::string s1("xxxx");

	lt1.push_back(s1);//左值
	lt1.push_back(move(s1));//右值

	cout << endl;
	yss::string s2("xxxx");
	lt1.emplace_back(s2);//左值
	lt1.emplace_back(move(s2));//右值

	cout << endl;
	lt1.push_back("1111");//右值
	lt1.emplace_back("1111");//右值

	return 0;
}

在这里插入图片描述
对于深拷贝类对象,如果插入的是类对象时,两个没有区别;如果插入的是对象的参数,那么emplace系列函数会减少一次移动构造

2️⃣浅拷贝类对象

int main()
{
	list<Date> lt1;
	
	Date d1(1, 1, 1);
	lt1.push_back(d1);//左值
	lt1.push_back(move(d1));//右值

	cout << endl;
	Date d2(2, 2, 2);
	lt1.emplace_back(d2);//左值
	lt1.emplace_back(move(d2));//右值

	cout << endl;
	lt1.push_back({ 3, 3, 3 });//构造+拷贝构造
	lt1.emplace_back(3, 3, 3);//构造

	return 0;
}

注意:emplace_back不支持使用列表初始化

在这里插入图片描述

对于浅拷贝类对象,如果插入的是类对象时,两个没有区别;如果插入的是对象的参数,那么emplace系列函数会减少一次拷贝构造

总结一下(以emplace_back为代表):

  • emplace_back和push_back都可以实现尾插,但是emplace_back的参数不仅可以接收左值和右值,还可以接收参数包
  • 直接插入对象参数的情况下,如果是浅拷贝类对象,减少了一次拷贝构造;如果是深拷贝类对象,减少了一次移动构造
  • 综合前面两点,emplace系列函数在性能上更加优越

相关推荐

  1. C++ 可变参数模板

    2024-04-02 17:36:06       13 阅读
  2. C++可变参数模板省略号

    2024-04-02 17:36:06       16 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-04-02 17:36:06       18 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-04-02 17:36:06       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-04-02 17:36:06       18 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-04-02 17:36:06       20 阅读

热门阅读

  1. 深入了解Flutter中的Sealed Class及其使用

    2024-04-02 17:36:06       13 阅读
  2. linux脚本打包

    2024-04-02 17:36:06       12 阅读
  3. python系列教程220——哪种迭代最快

    2024-04-02 17:36:06       13 阅读
  4. 问答系统开发:基于深度学习的文本理解与生成

    2024-04-02 17:36:06       12 阅读
  5. 【leetcode面试经典150题】2.移除元素(C++)

    2024-04-02 17:36:06       9 阅读
  6. R语言提取站点的nc文件时间序列数据

    2024-04-02 17:36:06       13 阅读
  7. 每日一题 --- 用栈实现队列[力扣][Go]

    2024-04-02 17:36:06       13 阅读
  8. docker-打包&部署

    2024-04-02 17:36:06       9 阅读
  9. Python 踩坑记

    2024-04-02 17:36:06       16 阅读