【C++庖丁解牛】C++11---新的类的功能 | 可变参数模板

news2024/12/25 12:18:03
🍁你好,我是 RO-BERRY
📗 致力于C、C++、数据结构、TCP/IP、数据库等等一系列知识
🎄感谢你的陪伴与支持 ,故事既有了开头,就要画上一个完美的句号,让我们一起加油

在这里插入图片描述


目录

  • 1.新的类功能
    • 1.1 默认成员函数
    • 1.2 类成员变量初始化
    • 1.3 强制生成默认函数的关键字default:
    • 1.4 禁止生成默认函数的关键字delete:
  • 2.可变参数模板
    • 2.1递归函数方式展开参数包
    • 2.2 逗号表达式展开参数包
    • 2.3 STL容器中的empalce相关接口函数


1.新的类功能

1.1 默认成员函数

原来C++类中,有6个默认成员函数:

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

最后重要的是前4个,后两个用处不大。默认成员函数就是我们不写编译器会生成一个默认的。

💧C++11 新增了两个:移动构造函数和移动赋值运算符重载。

针对移动构造函数和移动赋值运算符重载有一些需要注意的点如下:

  • 如果你没有自己实现移动构造函数,且没有实现析构函数 、拷贝构造、拷贝赋值重载中的任意一个。那么编译器会自动生成一个默认移动构造。默认生成的移动构造函数,对于内置类型成员会执行逐成员按字节拷贝,自定义类型成员,则需要看这个成员是否实现移动构造,如果实现了就调用移动构造,没有实现就调用拷贝构造。
  • 如果你没有自己实现移动赋值重载函数,且没有实现析构函数 、拷贝构造、拷贝赋值重载中的任意一个,那么编译器会自动生成一个默认移动赋值。默认生成的移动构造函数,对于内置类型成员会执行逐成员按字节拷贝,自定义类型成员,则需要看这个成员是否实现移动赋值,如果实现了就调用移动赋值,没有实现就调用拷贝赋值。(默认移动赋值跟上面移动构造完全类似)
  • 如果你提供了移动构造或者移动赋值,编译器不会自动提供拷贝构造和拷贝赋值。

1.2 类成员变量初始化

C++11允许在类定义时给成员变量初始缺省值,默认生成构造函数会使用这些缺省值初始化,这
个我们在类和对象默认就讲了,这里就不再细讲了。
在这里插入图片描述

1.3 强制生成默认函数的关键字default:

C++11可以让你更好的控制要使用的默认函数。假设你要使用某个默认的函数,但是因为一些原因这个函数没有默认生成。比如:我们提供了拷贝构造,就不会生成移动构造了,那么我们可以使用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(const Person& p) = default;
	Person(Person&& p) = default;
	Person& operator=(Person&& p) = default;
private:
	mystring::string _name;
	int _age;
};

int main()
{
	Person s1;
	Person s2 = s1;
	Person s3 = std::move(s1);
	return 0;
}

1.4 禁止生成默认函数的关键字delete:

如果能想要限制某些默认函数的生成,在C++98中,是该函数设置成private,并且只声明不定义,这样只要其他人想要调用就会报错。在C++11中更简单,只需在该函数声明加上=delete即可,该语法指示编译器不生成对应函数的默认版本,称=delete修饰的函数为删除函数。

class Person
{
public:
	Person(const char* name = "", int age = 0)
		:_name(name)
		, _age(age)
	{}
	Person(const Person& p) = delete;
private:
	mystring::string _name;
	int _age;
};
int main()
{
	Person s1;
	Person s2 = s1;
	Person s3 = std::move(s1);
	return 0;
}

在这里插入图片描述

2.可变参数模板

C语言的可变参数是printf那一个系列
在这里插入图片描述

C++11的新特性可变参数模板能够让您创建可以接受可变参数的函数模板和类模板,相C++98/03,类模版和函数模版中只能含固定数量的模版参数,可变模版参数无疑是一个巨大的改进。然而由于可变模版参数比较抽象,使用起来需要一定的技巧,所以这块还是比较晦涩的。现阶段呢,我们掌握一些基础的可变参数模板特性就够我们用了

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

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

上面的参数args前面有省略号,所以它就是一个可变模版参数,我们把带省略号的参数称为“参数包”,它里面包含了0到N(N>=0)个模版参数。我们无法直接获取参数包args中的每个参数的,只能通过展开参数包的方式来获取参数包中的每个参数,这是使用可变模版参数的一个主要特点,也是最大的难点,即如何展开可变模版参数。由于语法不支持使用args[i]这样方式获取可变参数,所以我们的用一些奇招来一一获取参数包的值。

//参数包是0-N个参数
template <class ...Args>
void ShowList(Args... args)
{}

int main()
{
	ShowList();
	ShowList(1);
	ShowList(1,'A');
	ShowList(1,'A',string("sort"));

	return 0;
}

看起来似乎这个很好用,但是函数体如何实现呢?如何去识别传了多少个参数并且参数都是什么类型,这就很麻烦。

我们使用sizeof...(args)可以算出有多少个参数

//参数包是0-N个参数
template <class ...Args>
void ShowList(Args... args)
{
	cout << sizeof...(args) << endl;
}

int main()
{
	ShowList();
	ShowList(1);
	ShowList(1,'A');
	ShowList(1,'A',string("sort"));

	return 0;
}

在这里插入图片描述
我们光知道个数是没用的,我们还得取到里面的值

2.1递归函数方式展开参数包

// 递归终止函数
template <class T>
void ShowList(const T& t)
{
	cout << t << endl;
}
// 展开函数
template <class T, class ...Args>
void ShowList(T value, Args... args)
{
	cout << value << " ";
	ShowList(args...);
}
int main()
{
	ShowList(1);
	ShowList(1, 'A');
	ShowList(1, 'A', std::string("sort"));
	return 0;
}

在这里插入图片描述

2.2 逗号表达式展开参数包

这种展开参数包的方式,不需要通过递归终止函数,是直接在expand函数体中展开的, PrintArg不是一个递归终止函数,只是一个处理参数包中每一个参数的函数。这种就地展开参数包的方式实现的关键是逗号表达式。我们知道逗号表达式会按顺序执行逗号前面的表达式。

expand函数中的逗号表达式:(printarg(args), 0),也是按照这个执行顺序,先执行printarg(args),再得到逗号表达式的结果0。

同时还用到了C++11的另外一个特性——初始化列表,通过初始化列表来初始化一个变长数组, {(printarg(args), 0)...}将会展开成((printarg(arg1),0), (printarg(arg2),0)(printarg(arg3),0), etc... ),最终会创建一个元素值都为0的数组int arr[sizeof...(Args)]。由于是逗号表达式,在创建数组的过程中会先执行逗号表达式前面的部分printarg(args)打印出参数,也就是说在构造int数组的过程中就将参数包展开了,这个数组的目的纯粹是为了在数组构造的过程展开参数包


template <class T>
void PrintArg(T t)
{
	cout << t << " ";
}
//展开函数
template <class ...Args>
void ShowList(Args... args)
{
	int arr[] = { (PrintArg(args), 0)... };
	cout << endl;
}
int main()
{
	ShowList(1);
	ShowList(1, 'A');
	ShowList(1, 'A', std::string("sort"));
	return 0;
}

在这里插入图片描述

2.3 STL容器中的empalce相关接口函数

vector::emplace_back
list::emplace_back

  • emplace_back是C++中vector容器的一个成员函数,用于在容器的末尾直接构造一个新的元素。与push_back函数不同的是,emplace_back函数可以直接在容器中构造元素,而不需要先创建一个临时对象。

  • emplace_back函数接受的参数是构造元素所需的参数,它会将这些参数传递给元素类型的构造函数来创建新的元素。这样可以避免创建临时对象和拷贝操作,提高了性能。

  • 使用emplace_back函数时,需要注意传递的参数类型必须与容器中元素类型的构造函数参数匹配。

template <class... Args>
void emplace_back(Args&&... args);

那么emplace_back与push_back有啥区别呢?

  • 单个元素时没什么区别
int main()
{
	std::list<mystring::string> lt1;

	mystring::string s1("xxxx");
	lt1.push_back(s1);
	lt1.push_back(move(s1));
	cout << "============================" << endl;

	mystring::string s2("xxxx");
	lt1.emplace_back(s2);
	lt1.emplace_back(move(s2));

	return 0;
}

在这里插入图片描述

  • 使用复合参数也是差不多的
int main()
{
	std::list<mystring::string> lt1;

	mystring::string s1("xxxx");
	lt1.push_back(s1);
	lt1.push_back(move(s1));
	cout << "============================" << endl;

	mystring::string s2("xxxx");
	lt1.emplace_back(s2);
	lt1.emplace_back(move(s2));
	cout << "============================" << endl;

	std::list<pair<mystring::string,mystring::string>> lt2;
	pair<mystring::string, mystring::string> kv1("xxxxx", "zzzz");

	lt2.push_back(kv1);
	lt2.push_back(move(kv1));
	cout << "============================" << endl;

	pair<mystring::string, mystring::string> kv2("xxxxx", "zzzz");
	lt2.emplace_back(kv2);
	lt2.emplace_back(move(kv2));
	cout << "============================" << endl;


	return 0;
}

在这里插入图片描述

  • emplace独特用法

emplace可以直接构造,这样可以避免创建临时对象和拷贝操作,提高了性能。

	lt2.emplace_back("xxxx","yyyy");

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1635022.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

浏览器预加载器如何使页面加载速度更快

预加载器&#xff08;也称为推测或前瞻预解析器&#xff09;可能是浏览器性能有史以来最大的改进。 那么什么是预加载器以及它如何提高性能呢&#xff1f; 浏览器如何加载网页 网页充满了依赖关系——在下载相关的CSS之前页面无法开始渲染&#xff0c;然后当遇到脚本时HTML解…

零基础该如何自学linux运维?

零基础该如何自学linux运维&#xff1f;以下是建议帮助你入门Linux运维的一些建议。 一、自学建议&#xff1a; 理解基础概念&#xff1a;首先&#xff0c;你需要对Linux操作系统的基本概念有所了解&#xff0c;包括文件系统、用户权限、进程管理等。安装Linux系统&#xff1…

若依前后端部署系统--详细附图

一、后端部署 1、在ruoyi项目的Maven中的生命周期下双击package.bat打包Web工程&#xff0c;生成jar包文件。 提示打包成功 2、多模块版本会生成在ruoyi/ruoyi-admin模块下target文件夹,我们打开目录ruoyi-admin/taget&#xff0c;打开cmd&#xff0c;运行java -jar jar包名称…

【C语言进阶】程序编译中的预处理操作

&#x1f4da;作者简介&#xff1a;爱编程的小马&#xff0c;正在学习C/C&#xff0c;Linux及MySQL.. &#x1f4da;以后会将数据结构收录为一个系列&#xff0c;敬请期待 ● 本期内容讲解C语言中程序预处理要做的事情 目录 1.1 预处理符号 1.2 #define 1.2.1 #define定义标识…

【Docker学习】docker stats

命令&#xff1a; docker container stats 描述&#xff1a; 显示容器资源使用的状态&#xff08;实时&#xff09; 用法&#xff1a; docker container stats [OPTIONS] [CONTAINER...] 别名&#xff1a; docker stats(docker的一些命令可以简写&#xff0c;docker stats就等同…

Odoo17开发环境搭建

1.先下载godoo17_20240227_02.zip压缩包&#xff0c;里面包含了项目用到的所有的插件了&#xff0c;直接使用这个包即可。 下载地址&#xff1a;https://download.csdn.net/download/java173842219/89242257 2.解压该压缩包&#xff0c;目录如下&#xff1a; 3.下载pycharm并…

如何保证Redis双写一致性?

目录 数据不一致问题 数据库和缓存不一致解决方案 1. 先更新缓存&#xff0c;再更新数据 该方案数据不一致的原因 2. 先更新数据库&#xff0c;再更新缓存 3. 先删除缓存&#xff0c;再更新数据库 延时双删 4. 先更新数据库&#xff0c;再删除缓存 该方案数据不一致的…

声明式事务(@Transactional)使用时需要注意的坑

前言 上两篇文章已经详细分析了申明式事务的实现原理&#xff0c;知道了底层原理之后&#xff0c;现在就可以开始使用申明式事务去简化我们的代码了。但是在使用Transactional注解的时候也会经常遇到一些问题&#xff0c;有些问题不仔细测试观察的话还不容易发现&#xff0c;比…

Windows vbs脚本定时给焦点窗口发送消息

直接上脚本代码,你们可以自己看着改 MsgInputbox("message1") Msg1Inputbox("message2") numInputbox("number")a1 bnumset wshshellCreateObject("wscript.shell") 创建Windows的shell对象打开shell窗口 wscript.sleep 5000for ia t…

vue本地调试devtools

一、谷歌浏览器加载扩展程序 二、把解压的压缩包添加即可&#xff0c;重启浏览器 三、启动前端本地项目&#xff0c;即可看到Vue小图标

Linux|awk 特殊模式“BEGIN 和 END”

引言 在本文[1]&#xff0c;我们将介绍Awk的更多特性&#xff0c;特别是两个特殊的模式&#xff1a;BEGIN和END。 这些独特的功能在我们努力扩展和深入探索构建复杂Awk操作的多种方法时&#xff0c;将大有裨益。 实例 让我们从Awk系列的开篇回顾开始&#xff0c;回想一下&#…

SSL证书一般是怎么收费的?

SSL证书的费用通常按照以下几个因素决定&#xff1a; 1. 证书类型&#xff1a; - 域名验证&#xff08;DV&#xff09;证书&#xff1a;这是最基本的类型&#xff0c;仅验证域名所有权&#xff0c;费用一般在几十到几百之间. - 组织验证&#xff08;OV&#xff09;证书&#xf…

【触摸案例-手势解锁案例-九宫格 Objective-C语言】

一、手势解锁案例,九宫格,我们先来分析一下怎么实现: 首先呢,我们先来运行一下, 这一块儿,上面的这九个东西,肯定是要有一个九宫格的一个算法的问题,然后呢,上边的这九个小圆圈儿,这是什么东西,Button,为什么是Button,因为可以点,是吗,就因为这个?实际上,你用…

LeetCode55:跳跃游戏

题目描述 给你一个非负整数数组 nums &#xff0c;你最初位于数组的 第一个下标 。数组中的每个元素代表你在该位置可以跳跃的最大长度。 判断你是否能够到达最后一个下标&#xff0c;如果可以&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。 解题思想 每次…

软考-信息系统项目管理师-论文技术架构模板(60天备考第26天)

分享一段信息系统项目管理师论文项目技术架构描述的万能模板&#xff0c;供大家参考。距离考试还有二十八天&#xff0c;如果论文写不好的可以加微进论文指导群学习论文写作。 该系统前端基于Vue开发&#xff0c;后端基于java开发&#xff0c;前后端分离部署。整体采用B/S架构&…

【webrtc】MessageHandler 5: 基于线程的消息处理:以PeerConnection信令线程为例

peerconn的信令是通过post 消息到自己的信令线程消息来处理的PeerConnectionMessageHandler 是具体的处理器G:\CDN\rtcCli\m98\src\pc\peer_connection_message_handler.hMachinery for handling messages posted to oneself PeerConnectionMessageHandler 明确服务于 signalin…

公网ip申请ssl仅260

现在很多网站都已经绑定域名&#xff0c;因此使用的都是域名SSL证书保护网站传输信息安全&#xff0c;而没有绑定域名只有公网IP地址的网站想要保护传输信息安全就要申请IP SSL证书。IP SSL证书也是由正规CA认证机构颁发的数字证书&#xff0c;用来保护用户的隐私以及数据安全&…

容器工作流

背景 目前某平台使用计算容器和解析容器&#xff0c;这两种容器目前通过rabbitmq消息来进行链接&#xff0c;形成容器工作流&#xff0c;使用容器工作流框架可以省去两个容器中间环节的控制&#xff0c;不需要再使用java代码对容器的操作&#xff0c;通过容器工作流框架即可控…

资深项目经理15年心得:管理需求变更5大技巧

高效管理需求变更对项目管理至关重要。通过严格的变更控制&#xff0c;确保所有需求变更都与项目目标和范围保持一致&#xff0c;避免偏离原定计划&#xff0c;有助于项目按既定目标顺利推进。能够及时评估变更对项目的影响&#xff0c;有利于减低项目延期和超支的风险&#xf…

特斯拉PIXCELL矩阵大灯擎耀远程控制技术照亮未来智能之光

在科技的浪潮中&#xff0c;特斯拉这个名字如同一道闪电&#xff0c;照亮了新能源汽车的天空。而在这片星空中&#xff0c;特斯拉PIXCELL矩阵大灯则如同一颗璀璨的星辰&#xff0c;以其独特的创新技术和卓越的性能&#xff0c;为驾驶者提供了前所未有的照明体验。矩阵大灯技术如…