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

🍁你好,我是 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.hqwc.cn/news/659501.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

合泰杯(HT32F52352)RTC的应用(计时)--->掉电不丢失VBAT(代码已经实现附带源码)

摘要 在HT32F52352合泰单片机开发中&#xff0c;rtc在网上还是挺少人应用的&#xff0c;找了很久没什么资料&#xff0c;现在我根据手册和官方的代码进行配置理解。 RTC在嵌入式单片机中是一个很重要的应用资源。 记录事件时间戳&#xff1a;RTC可以记录事件发生的精确时间&…

什么是域名解析?域名解析的完整流程是什么?如何清理DNS缓存?(附源码)

目录 1、什么是域名&#xff1f; 2、为什么使用域名&#xff1f; 3、域名解析的完整流程 4、调用gethostbyname系统接口将域名解析成IP地址 5、为什么需要清理系统DNS缓存&#xff1f; 6、使用cmd命令清理DNS缓存 7、通过代码去清除系统DNS缓存 C软件异常排查从入门到精…

【入坑存内计算一键指南】存内计算运算原理:算法优化、存储介质探究

一.存内计算 存内计算是一种计算方式&#xff0c;它将计算单元嵌入到内存当中。在传统的计算机运行体系中&#xff0c;如冯诺依曼体系&#xff0c;计算机通常包括存储单元和计算单元两部分。计算机进行运算时&#xff0c;需要先将数据存入主存储器&#xff0c;然后按照顺序从…

Vue3框架

Vue3框架 一.使用create-vue搭建Vue3项目二.组合式API - setup选项1.setup选项的写法和执行时机2.setup中写代码的特点3. script setup 语法糖 三.组合式API - reactive和ref函数1. reactive2. ref3. reactive 对比 ref 四.组合式API - computed五.组合式API - watch1. 侦听单个…

数据结构——时间复杂度与空间复杂度

文章目录 一、算法效率算法的复杂度 二、时间复杂度1.时间复杂度的概念2.大O的渐进表示法3.例子 三、空间复杂度1.空间复杂度概念2.例子 四、常见复杂度对比 一、算法效率 算法的复杂度 算法在编写成可执行程序后&#xff0c;运行时需要耗费时间资源和空间(内存)资源 。 因此…

交叉调制少样本图像生成用于结直肠组织分类

文章目录 Cross-Modulated Few-Shot Image Generation for Colorectal Tissue Classification摘要方法实验结果 Cross-Modulated Few-Shot Image Generation for Colorectal Tissue Classification 摘要 提出问题&#xff1a; 针对罕见癌症组织的组织病理训练数据稀缺问题&…

MS8241/MS8242高速、高输出电流、电压反馈放大器

产品简述 MS8241/MS8242 是一颗高速的电压反馈放大器&#xff0c;具有电流 反馈放大器的高速转换特性&#xff0c;可以应用在所有传统的电压反馈运 放应用方案中。 MS8241/MS8242 能够稳定工作在低增益环路下 &#xff08;增益为 2 和 -1 &#xff09;&#xff0c;仅消耗…

DVWA靶场

DVWA是指Damn Vulnerable Web Application&#xff0c;是一个用于教育和训练网络安全人员的虚拟漏洞应用程序。DVWA模拟了一个包含了多种常见Web安全漏洞的虚拟环境&#xff0c;包括SQL注入、XSS攻击、CSRF攻击等等。通过使用DVWA&#xff0c;安全人员可以学习和实践各种Web安全…

Agent AI智能体:未来社会的无形引领者

目录 前言1. 智能体说明1.1 定义1.2 作用1.3 类型介绍1.4 核心技术 2. 技术进步与创新2.1 机器学习的进步2.2 深度学习与神经网络2.3 强化学习2.4 转移学习与多任务学习2.5 自然语言处理(NLP)的革新2.6 知识图谱与推理 3. 行业领域应用场景3.1 游戏行业3.2 医疗健康3.3 金融服务…

红魔8/8Pro/8SPro手机升级安卓14版RedMagic9.0系统+降级出厂救砖刷机

红魔8系列手机也终于引来了安卓14系统的更新&#xff0c;该系统为最新的RedMagic9.0&#xff0c;目前属于公测版本&#xff0c;如果你已经升级了官方UI8.0最新版系统&#xff0c;并且拥有公测资格&#xff0c;可以直接在线检测到最新版UI9.0系统。9.0系统目前对比之前的8.0的版…

电商智能化:阿里巴巴API在商品详情获取中的革命性应用

接口应用场景——电商产品定价 电商平台产品的定价问题是很多品牌非常重视的一个问题&#xff0c;产品的定价取决于很多因素&#xff0c;包括成本、供需情况、促销策略及竞争对手的价格等。因此&#xff0c;想要更合理地定价&#xff0c;品牌需要获取到影响产品定价的各类数据…

qt5-入门-2D绘图-基础

参考&#xff1a; QPainter_w3cschool https://www.w3cschool.cn/learnroadqt/k7zd1j4l.html C GUI Programming with Qt 4, Second Edition 本地环境&#xff1a; win10专业版&#xff0c;64位&#xff0c;Qt 5.12 代码已经测试通过。其他例子日后更新。 目录 基础知识penb…