C++ —— Lambda 表达式

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

🎁个人主页:工藤新一¹

🔍系列专栏:C++面向对象(类和对象篇)

🌟心中的天空之城,终会照亮我前方的路

🎉欢迎大家点赞👍评论📝收藏⭐文章


Lambda

一、Lambda表达式语法

lambda 表达式本质是一个匿名函数对象,跟普通函数不同的是他可以定义在函数内部。lambda 表达式语法使用层而言没有类型,所以我们⼀般是⽤ auto 或者模板参数定义的对象去接收 lambda 对象

  • lambda 表达式特点:轻量级
  • 快速定义一个匿名函数对象(也被称作:CLosure 闭包

lambda 表达式的格式:[capatrue-list](parameters)-> return type { function body }

  • [capatrue-list]: 捕捉列表,该列表总是出现在 [lambda] 函数的开始位置,编译器根据 [] 来判断接下来的代码是否为 lambda 函数,捕捉列表能够捕捉上下文中的变量 供 lambda 函数使用,捕捉列表可以传值和传引用捕捉(注意:即使捕捉列表为空也不能被省略)

  • (parameters): 参数列表,与普通函数的参数列表功能类似,如果不需要参数传递,那么即可连同 () 一起省略

  • -> return type: 返回值类型,用追踪返回类型形式声明函数的返回值类型,没有返回值时此部分可省略。一般返回值类型明确的情况下,也可省略,由编译器对返回类型进行推导

  • { function body }: 函数体,函数体内的实现跟普通函数完全类似,在该函数体内,除了使用其参数外,还可使用所有捕获到的变量,函数体为空时也不能被省略。

在这里插入图片描述


在这里插入图片描述


在这里插入图片描述


二、Lambda 表达式的应用

​ 在学习 lambda 表达式之前,我们的使⽤的可调⽤对象只有 函数指针仿函数对象,函数指针的类型定义起来⽐较⿇烦,仿函数要定义⼀个类,相对会⽐较⿇烦。使用 lambda 去定义可调⽤对象,既简单又方便

lambda 在很多其他地⽅⽤起来也很好⽤,⽐如 线程 中定义线程的执⾏函数逻辑,智能指针 中定制 删除器 等,lambda 的应⽤还是很⼴泛的,以后我们会不断接触到

在这里插入图片描述


在这里插入图片描述


在这里插入图片描述


在这里插入图片描述


在这里插入图片描述


在这里插入图片描述


三、捕捉列表

3.1概念与功能描述

lambda 表达式中默认只能用 lambda 函数体和参数中的变量,如果想运⽤外层作⽤域中的变量则需要进行 “捕捉”

  • 第一种捕捉方式:在捕获列表中显示 传值捕获(变量只读状态)传引用捕获,捕获多个变量用逗号进行分割。[ x, y, &z ] 表示 xy 值捕获,z 引用捕获
  • 第⼆种捕捉方式:在捕捉列表中隐式捕捉,在捕捉列表写⼀个 = 表⽰隐式值捕捉(将变量全部变为值捕捉),在捕捉列表 写⼀个 & 表⽰隐式引用捕捉,这样我们 lambda 表达式中使用的那些变量,编译器就会对其进行⾃动捕捉

注意:隐式捕获,不是将程序中的所有变量都捕捉到 **lambda** 表达式中,而是需要哪个,捕获哪个

  • 第三种捕捉方式:在捕捉列表中混合使⽤隐式捕捉和显⽰捕捉, [ = , &x ] 表⽰其他变量隐式值捕捉, x 引⽤捕捉;[ &, x, y ] 表⽰其他变量引⽤捕捉,xy 值捕捉。当使⽤混合捕捉时,第⼀个元素必须是 & 或 =,并且 & 混合捕捉时,后⾯的捕捉变量必须是值捕捉,同理 = 混合捕捉时,后⾯的捕捉变量必须是引⽤捕捉。

在这里插入图片描述


在这里插入图片描述


在这里插入图片描述


  • lambda 表达式在函数局部域中,他可以捕捉 lambda 位置之前定义的变量,不能捕捉静态局部变量和全局变量,静态局部变量和全局变量也不需要捕捉,lambda 表达式中可以直接使用。这也意味着**lambda** 表达式如果定义在全局位置,捕获列表必须为空

在这里插入图片描述


3.2mutable

  • 默认情况下, lambda 捕捉列表是被 const 修饰的,也就是说传值捕捉而来的对象不能被修改,mutable 加在参数列表的后⾯可以 取消其常量性,也就说使⽤该修饰符后,传值捕捉的对象就可以被修改了,但是修改还是形参对象,不会影响实参(类似值传递,返回的是自身数据的一份临时拷贝)— — 被 lambda 通过 “传值捕获” 的内部变量,本质是外部变量的一份临时拷贝。使用 mutable 修饰符后,参数列表不可省略(即使参数不能为空)。

在这里插入图片描述


四、Lambda 的原理

lambda 的原理和 范围for 很像,编译后从汇编指令层的⻆度看,压根就没有 lambda范围for 这样的东西。范围for 底层是迭代器,⽽lambda 底层是仿函数对象,也就说我们写了⼀个 lambda 以后,编译器会⽣成⼀个对应的仿函数的类

​ 仿函数的类名是编译按⼀定规则⽣成的,保证不同的 lambda ⽣成的类名不同,lambda 参数/返 回类型/函数体就是仿函数 operator() 的参数/返回类型/函数体, lambda 的捕获列表本质是⽣成 的仿函数类的成员变量,也就是说捕获列表的变量都是 lambda 类构造函数的实参,当然隐式捕获,编译器要看使⽤哪些就传那些对象

  • 上⾯的原理,我们可以透过 汇编层 了解⼀下
class Rate
{
public:
	Rate(double rate)
		: rate(rate) {}

	double operator()(double money, int year)
	{
		return money * rate * year;
	}

private:
	double rate;
};

int main()
{
	double rate = 0.49;

	//仿函数对象
	Rate r1(rate);
	r1(1000, 2);

	auto func1 = []()
		{
			cout << "Hello World" << endl;
		}; func1();


	//lambda
	//捕获列表中的rate,可以视作 lambda 类构造函数的参数传递
	auto r2 = [rate](double money, int year)
		{
			return money * rate * year;
		};
	r2(1000, 2);

	return 0;
}

在这里插入图片描述


  • 本质上都是给构造函数传参

在这里插入图片描述


在这里插入图片描述


  • 定义 lambda - 生成仿函数

  • 定义 lambda 对象 - 初始化仿函数对象

在这里插入图片描述


五、Lambda 捕获悬垂引用问题

在这里插入图片描述

此外,多线程中如果 捕获引用,也可能出现 引用失效 的问题,这会导致程序结果错误或访问异常等;而对于 传值捕获 则不会出现这种问题


在这里插入图片描述


在这里插入图片描述


在这里插入图片描述

🌟 各位看官好我是工藤新一¹呀~

🌈 愿各位心中所想,终有所致!

《餐馆点餐管理系统——基于Java和MySQL的课程设计解析》 在信息技术日益发达的今天,餐饮行业的数字化管理已经成为一种趋势。本次课程设计的主题是“餐馆点餐管理系统”,它结合了编程语言Java和数据库管理系统MySQL,旨在帮助初学者理解如何构建一个实际的、具有基本功能的餐饮管理软件。下面,我们将深入探讨这个系统的实现细节及其所涉及的关键知识点。 我们要关注的是数据库设计。在“res_db.sql”文件中,我们可以看到数据库的结构,可能包括菜品表、订单表、顾客信息表等。在MySQL中,我们需要创建这些表格并定义相应的字段,如菜品ID、名称、价格、库存等。此外,还要设置主键、外键来保证数据的一致性和完整性。例如,菜品ID作为主键,确保每个菜品的唯一性;订单表中的顾客ID和菜品ID则作为外键,与顾客信息表和菜品表关联,形成数据间的联系。 接下来,我们来看Java部分。在这个系统中,Java主要负责前端界面的展示和后端逻辑的处理。使用Java Swing或JavaFX库可以创建用户友好的图形用户界面(GUI),让顾客能够方便地浏览菜单、下单。同时,Java还负责与MySQL数据库进行交互,通过JDBC(Java Database Connectivity)API实现数据的增删查改操作。在程序中,我们需要编写SQL语句,比如INSERT用于添加新的菜品信息,SELECT用于查询所有菜品,UPDATE用于更新菜品的价格,DELETE用于删除不再提供的菜品。 在系统设计中,我们还需要考虑一些关键功能的实现。例如,“新增菜品和价格”的功能,需要用户输入菜品信息,然后通过Java程序将这些信息存储到数据库中。在显示所有菜品的功能上,程序需要从数据库获取所有菜品数据,然后在界面上动态生成列表或者表格展示。同时,为了提高用户体验,可能还需要实现搜索和排序功能,允许用户根据菜品名称或价格进行筛选。 另外,安全性也是系统设计的重要一环。在连接数据库时,要避免SQL注入攻击,可以通过预编译的PreparedStatement对象来执行SQL命令。对于用户输入的数据,需要进行验证和过滤,防止非法字符和异常值。 这个“餐馆点餐管理系统”项目涵盖了Java编程、数据库设计与管理、用户界面设计等多个方面,是一个很好的学习实践平台。通过这个项目,初学者不仅可以提升编程技能,还能对数据库管理和软件工程有更深入的理解。在实际开发过程中,还会遇到调试、测试、优化等挑战,这些都是成长为专业开发者不可或缺的经验积累
### C++11 Lambda表达式使用教程 #### 定义与基本语法 C++11引入了lambda表达式这一强大特性,使得开发者能够更加便捷地定义和创建匿名函数。这种新的功能不仅简化了代码编写过程,还提升了程序的功能性和执行效率[^1]。 Lambda表达式的通用形式如下: ```cpp [capture](parameters)->return_type { body } ``` 其中`capture`表示捕获列表,用于指定如何访问外部作用域中的变量;`parameters`代表参数列表;`->return_type`可选部分用来显式声明返回类型;最后是函数体`body`[^2]。 #### 捕获列表详解 捕获列表允许lambda表达式获取其所在环境内的局部变量或全局变量。常见的几种方式包括但不限于按值传递(`[var]`)、按引用传递(` [&var] `),以及默认全部按值/引用捕获(` [= ] / [&] `)[^3]。 ##### 示例:按值捕获 当采用按值的方式捕获时,意味着会复制一份原始数据供内部逻辑操作而不影响原值。 ```cpp #include <iostream> using namespace std; int main(){ int value = 42; auto func_by_value = [value]() { cout << "Value captured by copy: " << value << endl; }; ++value; // 修改外界的value func_by_value(); // 输出的是未修改前的数值 } ``` ##### 示例:按引用捕获 相反地,如果选择了按引用的形式,则任何对于被捕获变量的操作都会直接影响到实际存在的那个实例。 ```cpp #include <iostream> using namespace std; int main(){ int ref_val = 88; auto modify_ref = [&ref_val]() mutable{ ++ref_val; cout << "Modified reference-captured variable to : " << ref_val << endl; }; modify_ref(); } ``` #### 实际应用场景举例 考虑这样一个场景——我们需要交换两个整型数的位置而无需借助额外的空间开销。此时可以巧妙运用带有适当捕获机制的lambda表达式来完成任务[^5]。 ```cpp void swapTwoNumbers(int &num1, int &num2){ auto swapper = [&](int &a, int &b){ int temp=a;a=b;b=temp; }; swapper(num1,num2); } // 或者更为简洁的做法直接在lambda体内处理 auto direct_swap=[&](int &x,int &y){swap(x,y);} direct_swap(a,b); ``` #### 关于赋值行为的重要注意事项 值得注意的一点在于,尽管可以通过拷贝构造的方式来生成另一个具有相同行为的新对象,但是不同lambda表达式之间不允许互相赋值,即便它们看起来拥有完全一致的结构和语义[^4]。 ```cpp auto hello_world_1=[](){std::cout<<"Hello World"<<std::endl;}; auto hello_world_2=[](){std::cout<<"Hello World"<<std::endl;}; // 下面这行会导致编译错误 //hello_world_1=hello_world_2; ```
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值