modern C++:闭包与匿名函数

最近放假在写一个小项目,用到了闭包和匿名函数的知识,记录一下

What?

匿名函数:匿名函数(英语:Anonymous Function)在计算机编程中是指一类无需定义标识符(函数名)的函数或子程序,普遍存在于多种编程语言中。C++从C++11开始支持。
闭包:闭包(英语:Closure),又称词法闭包(Lexical Closure)或函数闭包(function closures),是在支持函数编程的编程语言中实现词法绑定的一种技术。闭包在实现上是一个结构体,它存储了一个函数(通常是其入口地址)和一个关联的环境(相当于一个符号查找表)

匿名函数只是一个没有名字的函数,闭包是一个函数指针+配套环境,闭包是一个函数的实例。
当匿名函数内也有外界定义的变量时就变成了闭包,当闭包没有外界变量时可以优化成一个普通函数(不一定是匿名函数)

why?

闭包的存在是为了保护私有变量不被污染,形成不销毁的栈内存,里面的私有变量等信息保存下来。
匿名函数常用于回调函数、事件处理器、或者在需要临时定义函数的地方。它们有助于减少代码的复杂性,使代码更加简洁。

how?

匿名函数是一种技术,只有lambda表达式一种实现。
闭包是一种思想,可以用不同方式来实现

方法一:重载()操作符

C++允许进行操作符重载,可以将一个类的()操作符重载,就可以使这个类的实例可以调用,Eg:

#include <iostream>class Operator {public:int operator ()(int a, int b) {return a+b+bias;}private:int bias = 1;
};int main() {Operator op;std::cout << op(1, 2) << std::endl;return 0;
}

好奇,能不能将这个重载声明为static,试了一下发现报错说运算符不能是静态成员函数。

方法二:lambda表达式

C++11中引入了lambda表达式,lambda是函数式编程中的概念,用于定义匿名函数,Eg:

#include <iostream>
#include <functional>int main() {int bias = 1;std::function<int(int)> f = [bias](int x) { return x + bias; };// lambda函数,bias是捕获的变量,x是参数,// 如果不捕获变量,可以写成[=],如果捕获所有变量,可以写成[&]// 不捕获变量时,就是一个普通的函数,捕获后就变成了闭包std::cout << f(1) << std::endl;return 0;
}

方法三:参数绑定

C++11在标准库中新增了bind函数,std::bind是C++中实现函数参数绑定的一种方式,它允许你创建一个可调用的函数对象,该对象可以存储一部分参数,并在需要时再提供剩余的参数。这在某些情况下非常有用,比如在多线程编程中,你可能需要将参数和函数传递给不同的线程。

C++11之前boost中有参数绑定函数bind,可以调用boost::bind函数

使用bind定义闭包如下:

#include <iostream>
#include <functional>int add(int a, int b) {std::cout << "a: " << a << " b: " << b << std::endl;return a + b;
}int main()
{auto f = std::bind(add, std::placeholders::_2, std::placeholders::_1);// placeholders可以理解为占位符,_1表示第一个参数,_2表示第二个参数,以此类推// 上面的代码表示将add函数的第二个参数作为f的第一个参数,第一个参数作为f的第二个参数// 所以调用f(1,2)时,实际上调用的是add(2,1)std::cout << f(1,2) << std::endl;auto f2 = std::bind(add, 1, std::placeholders::_1);// 上面的代码表示将add函数的第一个参数固定为1,第一个参数作为f2的第二个参数std::cout << f2(2) << std::endl;auto f3 = std::bind(add, std::placeholders::_1, 2);// 上面的代码表示将add函数的第二个参数固定为2,第一个参数作为f3的第一个参数std::cout << f3(1) << std::endl;auto f4 = std::bind(add, 1, std::placeholders::_2);// 上面的代码表示将add函数的第一个参数固定为1,第二个参数作为f4的第二个参数// 所以调用时必须给两个参数std::cout << f4(1,2) << std::endl;return 0;
}

使用时要注意C++不会因为闭包而延长变量的生命周期,在lambda中引用一个已经释放了的变量是一种未定义行为,文档原话:
If a non-reference entity is captured by reference, implicitly or explicitly, and operator() of the closure object is invoked after the entity’s lifetime has ended, undefined behavior occurs. The C++ closures do not extend the lifetimes of objects captured by reference.

others

  • C语言
    C语言中没有语法可以支持闭包,但是C语言支持回调函数,可以通过回调函数来达到类似于闭包的效果。
    在C语言中,支持回调函数的库有时在注册时需要两个参数:一个函数指针,一个独立的void*指针用以保存用户数据。这样的做法允许回调函数恢复其调用时的状态。这样的惯用法在功能上类似于闭包,但语法上有所不同。
  • Python
    Python从一开始就支持闭包,在Python中经常用闭包来实现装饰器
import functools
import timedef logger(func):"""装饰器函数,用于记录函数的调用信息"""@functools.wraps(func)  # 保持原始函数的名称和文档字符串def wrapper(*args, **kwargs):print(f"Calling {func.__name__} at {time.ctime()}")result = func(*args, **kwargs)print(f"{func.__name__} returned {result}")return resultreturn wrapper# 使用装饰器
@logger
def add(a, b):"""Add two numbers."""return a + b# 调用被装饰的函数
result = add(3, 4)

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

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

相关文章

ENSP中OSPF配置

题目 划分网段&#xff0c;配置ip OSPF配置按照区域划分&#xff0c;这个网段也要按照区域个数划分&#xff0c;如这一题&#xff0c;分成两个区域&#xff0c;所以将192.168.1.0/24划分先为两个网段&#xff0c;然后在具体的划分区域中的网段。 以交换机为中心的三条线属于一…

go语言小练习——基于goroutine实现的Tcp聊天室

前言 博主最近没怎么写go&#xff0c;最近正好放暑假&#xff0c;写了一个小demo来复习一下&#xff0c;源码会放在资源了&#xff0c;大家按需取用。 服务端 package mainimport ("bufio""fmt""github.com/sirupsen/logrus""net"&…

爬虫学习前记----Python

引言 1.语言&#xff1a;python 2.学习资源&#xff1a;【Python爬虫】 3.爬虫日记&#xff1a; python内容 1.字符串输出 (1)引号问题 print("python") 输出&#xff1a;pythonprint(python) 输出&#xff1a;pythonprint(python"学习") 输出&…

[Qt] Qt Creator中,新建QT文件时选择界面模版下的各选项

在Qt Creator中&#xff0c;新建文件时选择界面模版下的各选项具有特定的意义&#xff0c;这些选项主要帮助开发者根据项目需求快速生成不同类型的文件。以下是对这些选项的详细解释&#xff1a; 0. Qt Item Model 意义&#xff1a;列表模型是Qt中用于表示和操作数据的强大抽…

C++多态中的构造函数和析构函数

常见问题 构造函数可以是虚函数吗&#xff1f; 答&#xff1a;构造函数不可以是虚函数。 原因&#xff1a; 构造对象时必须知道对象的实际类型&#xff0c;但是虚函数调用在运行时才能确定对象的实际类型&#xff0c;这会导致编译器无法确定对象的具体类型。 虚函数的执行依赖于…

Android 使用 Debug.startMethodTracing 分析方法耗时

参考 Generate Trace Logs by Instrumenting Your App 官网提供了 trace 工具来分析方法耗时。 生成 trace 文件 package com.test.luodemo.trace;import androidx.appcompat.app.AppCompatActivity;import android.os.Bundle; import android.os.Debug; import android.uti…

实体类:JavaBean

文章目录 什么是实体类&#xff1f;满足要求应用场景 什么是实体类&#xff1f; 就是仅仅只用来保存数据的Java类有getter setter 方法&#xff0c;就只能用它来存取数据 满足要求 成员变量都是private都有getter setter方法类中必须有public的无参构造函数 应用场景 实体…

js vue table单元格合并

实现效果 关键代码 <table classtable table-bordered><thead><tr><th>检测项目</th><th>详细说明</th><th>检测结果</th><th>检测说明</th></tr></thead><tbody><tr ng-repeatrow in…

【car】深入浅出学习机械燃油车知识、结构、原理、维修、保养、改装、编程

汽车的五大总成通常是指发动机、变速器、前后桥、车架和悬挂系统。 发动机&#xff1a;是汽车的动力来源&#xff0c;负责将燃料的化学能转化为机械能&#xff0c;驱动汽车行驶。常见的发动机类型有内燃机&#xff08;如汽油发动机、柴油发动机&#xff09;和电动机&#xff0…

解析.Bixi勒索病毒:威胁分析与防范策略

导言&#xff1a; 在当今数字化时代&#xff0c;勒索病毒成为了网络安全的一大威胁。最近出现的.bixi勒索病毒引发了广泛关注和担忧。本文将介绍这种新型勒索病毒的特点和传播方式&#xff0c;并提供有效的应对策略&#xff0c;帮助您保护个人和组织的数据安全。如不幸感染这个…

asp .net core 避免请求body数据量过大

方法1&#xff0c; 全局避免 引入包 dotnet add package Microsoft.AspNetCore.Http.Features using Microsoft.AspNetCore.Http.Features;public void ConfigureServices(IServiceCollection services) {services.Configure<FormOptions>(options >{// 设置允许的最…

ant design pro多页签功能

效果&#xff1a; 原理&#xff1a; 1、所有需要页签页面&#xff0c;都需要一个共同父组件 2、如何缓存&#xff0c;用的是ant的Tabs组件&#xff0c;在共同父组件中&#xff0c;实际是展示的Tabs组件 3、右键&#xff0c;用的是ant的Dropdown组件&#xff0c;当点击时&…

【数理统计】2-随机变量及其概率分布

文章目录 一、随机变量的定义和分类二、离散概率分布2.1 二项分布 (Binomial Distribution)2.2 伯努利分布 (Bernoulli Distribution)2.3 几何分布 (Geometric Distribution)2.4 负二项分布 (Negative Binomial Distribution)2.5 超几何分布 (Hypergeometric Distribution)2.6 …

在linux中查找 / 目录下的以.jar结尾的文件(find / -name *.jar)

文章目录 1、查找 / 目录下的以.jar结尾的文件 1、查找 / 目录下的以.jar结尾的文件 [rootiZuf6332h890vozldoxcprZ ~]# find / -name *.jar /etc/java/java-1.8.0-openjdk/java-1.8.0-openjdk-1.8.0.342.b07-1.el9_0.x86_64/lib/security/policy/limited/US_export_policy.ja…

【学习css2】grid布局-页面footer部分保持在网页底部

中间内容高度不够屏幕高度撑不开的页面时候&#xff0c;页面footer部分都能保持在网页页脚&#xff08;最底部&#xff09;的方法 1、首先上图看显示效果 2、奉上源码 2.1、html部分 <body><header>头部</header><main>主区域</main><foot…

在 Linux 上设置 RAID 阵列的全面指南

引言 在这篇博文中&#xff0c;我们将深入探讨如何在 Linux 上设置 RAID 阵列。本文将涵盖 RAID 的定义、架构、原理、应用场景、常见命令体系&#xff0c;并通过详细的实战模拟展示如何在 Linux 系统上实际操作。希望通过这篇文章&#xff0c;您能深入理解 RAID 技术&#xff…

Flutter实现局部刷新的几种方式

目录 前言 1.局部刷新的重要性 1.概念 2.重要性 2.局部刷新实现的几种方式 1.使用setState方法进行局部刷新 2.使用StatefulWidget和InheritedWidget局部刷新UI 3.ValueNotifier和ValueListenableBuilder 4.StreamBuilder 5.Provider 6.GetX 7.使用GlobalKey 前言 …

现在有什么副业可以让人快速上岸?可以试试这个行业上岸其实不难

人为什么要努力赚銭&#xff1f; 当你想结婚的时候&#xff0c; 你可以慢慢挑&#xff0c;不着急。 当父母年老遭遇大病的时候&#xff0c; 你有机会尽孝。 当孩子需要时&#xff0c;你不会囊中羞涩。 年轻时以为金钱最重要&#xff0c; 如今年纪大了&#xff0c;发现这…

CSS在页面中使用的三种方式:行内样式、内嵌式样式表、链接式样式表

CSS样式如何在页面中使用&#xff0c;包含三种方式&#xff1a;行内样式、内嵌式样式表、链接式样式表。 1、行内样式 行内样式是比较直接的一种样式&#xff0c;直接定义在 HTML 标签之内&#xff0c;并通过 style 属性来实现。这种方式比较容易学习&#xff0c;但是灵活性不…

【秋招突围】2024届秋招笔试-字节跳动笔试题-01-三语言题解(Java/Cpp/Python)

&#x1f36d; 大家好这里是清隆学长 &#xff0c;一枚热爱算法的程序员 &#x1f4bb; ACM银牌&#x1f948;| 多次AK大厂笔试 &#xff5c; 编程一对一辅导 ✨ 本系列打算持续跟新 秋招笔试题 &#x1f44f; 感谢大家的订阅➕ 和 喜欢&#x1f497; &#x1f4e7; 清隆这边最…