C++11第三弹:lambda表达式 | 新的类功能 | 模板的可变参数

news2024/11/24 6:06:36

在这里插入图片描述

🌈个人主页: 南桥几晴秋
🌈C++专栏: 南桥谈C++
🌈C语言专栏: C语言学习系列
🌈Linux学习专栏: 南桥谈Linux
🌈数据结构学习专栏: 数据结构杂谈
🌈数据库学习专栏: 南桥谈MySQL
🌈Qt学习专栏: 南桥谈Qt
🌈菜鸡代码练习: 练习随想记录
🌈git学习: 南桥谈Git

🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈
本科在读菜鸡一枚,指出问题及时改正

文章目录

  • lambda表达式
    • C++98中的一个例子
    • Lambda表达式基础语法
      • lambda表达式各部分说明
      • 捕获列表说明
  • 新的类功能
    • 默认成员函数
    • 强制生成默认函数的关键字default
    • 禁止生成默认函数的关键字delete
  • 模板的可变参数
    • C语言中的可变参数
    • C++中可变参数
      • 递归函数方式展开参数包
      • 逗号表达式展开参数包
    • emplace容器系列

lambda表达式

C++98中的一个例子

在C++98中,如果想要对一个数据集合中的元素进行排序,可以使用std::sort方法。

#include <algorithm>
#include <functional>
int main()
{
int array[] = {4,1,8,5,3,7,0,9,2,6};
// 默认按照小于比较,排出来结果是升序
std::sort(array, array+sizeof(array)/sizeof(array[0]));
// 如果需要降序,需要改变元素的比较规则
std::sort(array, array + sizeof(array) / sizeof(array[0]), greater<int>());
return 0;
}

如果待排序元素为自定义类型,需要用户定义排序时的比较规则:

struct Goods
{
	string _name; // 名字
	double _price; // 价格
	int _evaluate; // 评价
	Goods(const char* str, double price, int evaluate)
		:_name(str)
		, _price(price)
		, _evaluate(evaluate)
	{}
};

struct ComparePriceLess
{
	bool operator()(const Goods& gl, const Goods& gr)
	{
		return gl._price < gr._price;
	}
};

struct ComparePriceGreater
{
	bool operator()(const Goods& gl, const Goods& gr)
	{
		return gl._price > gr._price;
	}
};

int main()
{	
	vector<Goods> v = { { "苹果", 2.1, 5 }, { "香蕉", 3, 4 }, { "橙子", 2.2,
	3 }, { "菠萝", 1.5, 4 } };
	sort(v.begin(), v.end(), ComparePriceLess());
	sort(v.begin(), v.end(), ComparePriceGreater());
}

随着C++语法的发展,人们开始觉得上面的写法太复杂了,每次为了实现一个algorithm算法,都要重新去写一个类,如果每次比较的逻辑不一样,还要去实现多个类,特别是相同类的命名,这些都给编程者带来了极大的不便。因此,在C++11语法中出现了Lambda表达式。

Lambda表达式基础语法

lambda表达式书写格式:[capture-list] (parameters) mutable -> return-type { statement}

lambda表达式各部分说明

[capture-list] : 捕捉列表,该列表总是出现在lambda函数的开始位置,编译器根据[]来判断接下来的代码是否为lambda函数,捕捉列表能够捕捉上下文中的变量供lambda函数使用。
(parameters):参数列表。与普通函数的参数列表一致,如果不需要参数传递,则可以连同()一起省略
mutable:默认情况下,lambda函数总是一个const函数,mutable可以取消其常量性。使用该修饰符时,参数列表不可省略(即使参数为空)。
->returntype:返回值类型。用追踪返回类型形式声明函数的返回值类型,没有返回值时此部分可省略。返回值类型明确情况下,也可省略,由编译器对返回类型进行推导。
{statement}:函数体。在该函数体内,除了可以使用其参数外,还可以使用所有捕获到的变量。

int main()
{
	auto add1 = [](int a, int b)->int {return a + b; };
	auto add2 = [](int a, int b){return a + b; };   //返回值省略
	auto func1 = [] {cout << "hello world" << endl; };  // 没有参数,参数列表可以省略
	cout << add1(1, 2) << endl;
	cout << add2(2, 3) << endl;
	func1();
	return 0;
}

在这里插入图片描述
通过上述例子可以看出,lambda表达式实际上可以理解为无名函数,该函数无法直接调用,如果想要直接调用,可借助auto将其赋值给一个变量。

将上述排序修改成lambda表达式:

int main()
{	
	vector<Goods> v = { { "苹果", 2.1, 5 }, { "香蕉", 3, 4 }, { "橙子", 2.2,
	3 }, { "菠萝", 1.5, 4 } };

	auto priceLess = [](const Goods& g1, const Goods& g2) {return g1._price < g2._price; };
	sort(v.begin(), v.end(), priceLess);
}

在这里插入图片描述

在这里插入图片描述

捕获列表说明

在这里插入图片描述
捕捉列表描述了上下文中那些数据可以被lambda使用,以及使用的方式传值还是传引用。

  • [var]:表示值传递方式捕捉变量var

  • [=]:表示值传递方式捕获所有父作用域中的变量(包括this)

int main()
{
	int a = 1, b = 2, c = 3, d = 4, e = 5;
	auto func1 = [=]()
	{
		return a + b + c + d + e;
	};
	cout << func1();

	return 0;
}

在这里插入图片描述

  • [&var]:表示引用传递捕捉变量var
int main()
{
	int a = 1, b = 2;
	//引用方式捕捉
	auto swap3 = [&a, &b]() 
	{
		int tmp = a;
		a = b;
		b = tmp;
	};
	swap3();
	return 0;
}

在这里插入图片描述

引用捕捉,swap3 = [&a, &b]()这里的ab就是外部ab的别名,里面发生改变必定导致外面的ab发生变化。

  • [&]:表示引用传递捕捉所有父作用域中的变量(包括this)
int main()
{
	int a = 1, b = 2, c = 3, d = 4, e = 5auto func2 = [&]()
	{
		a++;
		b++;
		c++;
		d++;
		e++;
	};
	func2();
	cout << a << b << c << d << e << endl;

	return 0;
}

在这里插入图片描述

  • [this]:表示值传递方式捕捉当前的this指针

注意:

  1. 父作用域指包含lambda函数的语句块
  2. 语法上捕捉列表可由多个捕捉项组成,并以逗号分割。
    比如:[=, &a, &b]:以引用传递的方式捕捉变量a和b,值传递方式捕捉其他所有变量[&,a, this]:值传递方式捕捉变量a和this,引用方式捕捉其他变量
  3. 捕捉列表不允许变量重复传递,否则就会导致编译错误。比如:[=, a]:=已经以值传递方式捕捉了所有变量,捕捉a重复。
  4. 在块作用域以外的lambda函数捕捉列表必须为空。
  5. 在块作用域中的lambda函数仅能捕捉父作用域中局部变量,捕捉任何非此作用域或者非局部变量都会导致编译报错。
  6. lambda表达式之间不能相互赋值,即使看起来类型相同

新的类功能

默认成员函数

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

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

最后重要的是前4个,后两个用处不大。默认成员函数就是我们不写编译器会生成一个默认的。C++11 新增了两个:移动构造函数(移动拷贝)和移动赋值运算符重载(移动拷贝)。这个针对的是深拷贝的自定义类型对象,如:stringvectorlist等。

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

  1. 如果你没有自己实现移动构造函数,且没有实现析构函数 、拷贝构造、拷贝赋值重载中的任意一个。那么编译器会自动生成一个默认移动构造。默认生成的移动构造函数,(1)对于内置类型成员会执行逐成员按字节拷贝,(2)自定义类型成员,则需要看这个成员是否实现移动构造,如果实现了就调用移动构造,没有实现就调用拷贝构造。
  2. 如果你没有自己实现移动赋值重载函数,且没有实现析构函数 、拷贝构造、拷贝赋值重载中的任意一个,那么编译器会自动生成一个默认移动赋值。默认生成的移动构造函数,对于内置类型成员会执行逐成员按字节拷贝,自定义类型成员,则需要看这个成员是否实现移动赋值,如果实现了就调用移动赋值,没有实现就调用拷贝赋值。(默认移动赋值跟上面移动构造完全类似)
  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(Person && p) = default;
private:
	gwj::string _name;
	int _age;
};
int main()
{
	Person s1;
	Person s2 = s1;
	Person s3 = std::move(s1);
	return 0;
}

在这里插入图片描述

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

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

实现一个类只能在堆上创建对象:

class HeapOnly
{
public:
    // 静态方法,用于在堆上创建 HeapOnly 对象
    static HeapOnly* CreateObj()
    {
        return new HeapOnly;
    }

private:
    // 私有构造函数,禁止在类外部直接创建对象
    HeapOnly()
    {}

    int _a = 1;
};

int main()
{
    // HeapOnly ho1;           // 错误:构造函数是私有的,无法在类外部直接创建对象
    // HeapOnly* p1 = new HeapOnly;  // 错误:构造函数是私有的,无法直接调用 new 操作符

    HeapOnly* p2 = HeapOnly::CreateObj(); // 正确:通过静态方法创建对象

    return 0;
}

这个方法仍然有bug,不能确保创建的类是在堆上:

HeapOnly obj(*p2);//obj还是栈上的对象

通过 HeapOnly obj(*p2); 语句在栈上创建 HeapOnly 对象。这是因为 HeapOnly obj(*p2); 使用了拷贝构造函数,该构造函数是隐式定义的,并允许通过复制堆上的对象来创建栈上的对象。

解决这一问题,C++98中是将构造函数私有,并且只声明不实现:

在这里插入图片描述
HeapOnly obj(*p2); 这行代码会导致编译错误。虽然可以通过 CreateObj 在堆上创建对象,但由于拷贝构造函数没有定义,编译器无法进行对象的拷贝操作,因此这会引发错误。

C++11解决方式:在不希望调用的函数后面加上delete
拷贝构造函数:HeapOnly(const HeapOnly&) = delete; 删除了拷贝构造函数,禁止对象的拷贝操作。尝试复制 HeapOnly 对象会导致编译错误。
在这里插入图片描述

在C++标准库中,流对象不希望被拷贝:
在这里插入图片描述
继承和多态中的finaloverride关键字

模板的可变参数

C语言中的可变参数

C语言中也有可变参数的概念,他的底层是一个动态数组,存一个可变参数,然后一次解析动态可变参数。
在这里插入图片描述

C++中可变参数

C++中的可变参数不在函数中,而是在模板中体现。

一个基本可变参数的函数模板:

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

在这里插入图片描述

递归函数方式展开参数包

编译时,参数推导递归:

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;
}

在这里插入图片描述

调用过程,以第三个为例:
在这里插入图片描述

逗号表达式展开参数包

这种展开参数包的方式,不需要通过递归终止函数,是直接在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>
int PrintArg(T t)
{
	cout << t << " ";
	return 0;
}

//展开函数
template <class ...Args>
void ShowList(Args... args)
{
	int arr[] = { (PrintArg(args)... };  //参数包里面有几个值,PrintArg函数就调用几次,就会有几个返回值,arr就会开多大
	cout << endl;
}

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

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

emplace容器系列

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

int main()
{
	list<pair<gwj::string, int>> lt;
	pair<gwj::string, int> kv1("xxxxx", 1);
	lt.push_back(kv1);
	lt.push_back(move(kv1));

	cout << endl;

	//pair<gwj::string, int> kv2("xxxxx", 1);
	//lt.emplace_back(kv2);
	//lt.emplace_back(move(kv2));

	lt.emplace_back("xxxx", 1);
	return 0;
}

对于push_back需要传入pair,左值走拷贝构造,右值走移动构造。但是,emplace_back可以直接传入pair对象的参数。

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

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

相关文章

为什么RAG对下一代AI开发至关重要

RAG&#xff08;检索增强生成&#xff09;是一种突破性技术&#xff0c;它将信息检索与文本生成相结合&#xff0c;以提高人工智能系统的知识和准确性。利用 RAG 可以帮助开发人员确保应用程序响应具有最丰富的上下文和准确性&#xff0c;因为它可以访问原始模型训练之外的精选…

解决samba无权限创建文件问题

将我服务器利用samba工具映射到到电脑后&#xff0c;没有权限在特定的文件里写文件&#xff0c;比如在mcu这个文件夹里面没有写文件的权限。 查看mcu文件夹的用户属性&#xff0c;属于root属性。 rootzwzn2064-CVN-Z690D5-GAMING-PRO:/home/zwzn2064# ls -ll total 9714860 dr…

MySQL 自学笔记(入门基础篇,含示例)

目录 一、基础1.变量2.运算符 二、数据库与表1.创建数据库2.表的增删与修改&#xff08;1&#xff09;表的创建与删除&#xff08;2&#xff09;表的修改&#xff08;3&#xff09;其他指令 3.属性约束4.数据操作&#xff08;1&#xff09;数据更新&#xff08;2&#xff09;数…

Hive任务优化参数整理

Hive本身是个基于hdfs的结构化数据管理工具&#xff0c;虽然在后面的发展中允许底层接入其他的数据源&#xff0c;比如第三方数据服务这种基础架构&#xff0c;但是它从立意上来说&#xff0c;它不适合用来做高性能查询引擎&#xff0c;反而在传统离线数据仓库中它有着自身的优…

在连通无向图中寻找正反向各通过每条边一次的路径(中国邮递员问题)

在连通无向图中寻找正反向各通过每条边一次的路径(中国邮递员问题) 引言问题定义算法思路具体步骤第一步:找出所有奇度顶点第二步:将奇度顶点配对,并添加最短路径第三步:构造欧拉回路伪代码C语言实现引言 在图论中,中国邮递员问题(Chinese Postman Problem, CPP)是一…

高精度E4990A参数资料keysight是德e4990a阻抗分析仪

Keysight E4990A 阻抗分析仪的规格&#xff1a; 工作频率选项&#xff1a; 20 Hz 至 10 MHz20 Hz 至 20 MHz20 Hz 至 30 MHz20 Hz 至 50 MHz20 Hz 至 120 MHz选项分别为 010 / 020 / 030 / 050 / 120 测量参数&#xff1a;lZl、lYl、O、R、X、G、B、L、C、D、Q、复合 Z、复合…

DBA 的 AI 助手 | MySQL 清理 undo log 居然用了 10 个小时?

社区王牌专栏《一问一实验&#xff1a;AI 版》全新改版归来&#xff0c;得到了新老读者们的关注。其中不乏对 ChatDBA 感兴趣的读者前来咨询&#xff0c;表达了想试用体验 ChatDBA 的意愿&#xff0c;对此我们表示感谢 &#x1f91f;。 目前&#xff0c;ChatDBA 还在最后的准备…

keil调试技巧 keil中的debug调试技巧单片机调试技巧 定位单片机程序异常位置 定位程序异常错误原因计算程序执行时间多少

单片机常用调试技巧(B站UP分享) 如何查看程序运行时间 使用Event Recorder 组件 查看运行时间 https://blog.csdn.net/twx11213030422/article/details/114013796 https://blog.csdn.net/weixin_43866583/article/details/125874888?spm1001.2101.3001.6661.1&utm_med…

大数据开发概论

大数据开发概论 文章目录 大数据开发概论大数据定义大数据概念计算机组成原理软件和硬件概述操作系统 大数据定义 大数据(Big Data)&#xff0c;是指无法在一定时间范围内用常规软件工具进行捕捉、管理和处理的数据集合&#xff0c;因此需要新处理模式&#xff0c;该处理模式就…

SpinalHDL之数据类型(二)

本文作为SpinalHDL学习笔记第五十五篇,介绍SpinalHDL的Bits数据类型。 目录: 1.描述(Description) 2.声明(Declaration) 3.操作符(Operators) 1.描述(Description) Bits类型对应于没有算数意义的bits向量。 2.声明(Declaration) 声明bit向量的语法如下所⽰:([]中为可填…

让图片变成动画,@keyframes:动画制作

keyframes被称为关键帧&#xff0c;其类似于Flash中的关键帧。在CSS3中其主要以“keyframes”开头&#xff0c;后面跟着是动画名称加上一对花括号“{…}”&#xff0c;括号中是一些不同时间段样式规则。 语法&#xff1a;keyframes animationname {keyframes-selector{css-styl…

Windows terminal使用说明

1 terminal基本介绍 1 下载 从微软商店上下载的方式网速比较慢&#xff0c;一种直接的方式是直接用命令行运行命令 winget install --idMicrosoft.WindowsTerminal -e# Window Terminal 安装以及使用(2021最新) 2 ssh配置 # 使用Windows Terminal进行SSH登录 1 通过label…

网安学习与木马病毒

一、网安知识学习大点(B站大学) 现状&#xff1a;掌握基本的编程思想、了解过网络知识、听说过一些网安的东西 0、法律 1、操作系统&#xff08;Windows和Linux&#xff09; 2、数据通信&#xff08;网络&#xff09; 3、数据库&#xff08;mysql&#xff09; 4、一门编程…

高效诊断Linux性能问题

从uptime命令开始&#xff1b;这里的关键指标是平均负载&#xff0c;它显示了过去 1分钟&#xff0c;5分钟和15分钟内正在运行或等待资源的进程平均数量&#xff1b;如果这些数字持续高于CPU内核数&#xff0c;则可能表明进程正在争夺资源&#xff0c;提示我们使用其他工具深入…

“视频号小店:微信新零售生态中的创新引擎与未来展望“

随着数字技术的快速发展&#xff0c;微信视频号利用其社交特性和庞大的用户基础&#xff0c;正逐渐崭露头角&#xff0c;成为新零售行业的新宠。视频号小店&#xff0c;作为这一生态系统中的创新亮点&#xff0c;为商家提供了一个集商品展示、交易促成和营销策略于一体的全新商…

企业文件加密软件怎么选?2024超好用的十款企业文档加密软件推荐!臻品优选!

"数据如金需锁固&#xff0c;密文守护保安宁。" 文件加密软件是守护企业数据安全的重要利器&#xff0c;选择一款适合企业自身需求的加密软件&#xff0c;不仅能够提升数据安全性&#xff0c;还能优化团队协作效率&#xff0c;确保业务连续性。 本文特为您臻选2024年…

安卓framework美化手势导航侧滑返回UI

文章目录 手势导航的侧滑返回效果图原生效果如下:要实现的功能,: 实现代码1. 初始化代码2. 修改绘制的代码,进行箭头与退出UI的显示3. 拖动的时候手指上下移动时,箭头ui跟着移动 以下是一些其他可以美化安卓右滑手势拖动 UI 的方法&#xff1a;视觉效果方面形状和布局方面 安卓…

「电商运营」高客单和低客单产品及选品的运营区别

高客单和低客单产品在运营重点和选品方向上存在明显的差异。在实际运营中&#xff0c;需要根据产品的特点和市场需求&#xff0c;制定合适的运营策略和选品方向&#xff0c;以实现产品的成功运营和盈利。 一、高客单产品运营重点 1、目标客户精准定位 由于高客单产品价格较高…

深度解析Unix系统的基本概念及优缺点和原理

介绍 Unix系统是一种多用户、多任务、分时操作系统&#xff0c;起源于20世纪70年代初&#xff0c;由贝尔实验室开发。它具有强大的命令行接口和层次结构的文件系统&#xff0c;支持多种处理器架构&#xff0c;广泛应用于工程应用和科学计算等领域。 基本概念 一、Unix系统的起…

以实时,见未来——DolphinDB 2024 年度峰会圆满举办

2024年9月6日&#xff0c;“以实时&#xff0c;见未来”—— DolphinDB 2024 年度峰会在杭州圆满落下帷幕。本次峰会由主会场与三个专题分会场组成&#xff0c;众多金融机构领导与专家、行业领袖、高校与研究机构学者等近300位嘉宾共襄盛举&#xff0c;一同探讨数智化浪潮下金融…