C++ 函数对象、函数指针与Lambda表达式

news/2024/11/8 19:29:14/文章来源:https://www.cnblogs.com/runtimeerror/p/18535761

C++ 函数对象、函数指针与Lambda表达式

函数指针

函数指针(Function Pointer)是指向函数的指针变量。它可以存储函数的地址,并通过该指针变量来调用该函数。函数指针的声明使用指针符号 ,指向的类型为函数的返回类型和参数列表,如 int (funcPtr)(int, int);。函数指针的值可以指向相同返回类型和参数列表的函数。

类型名(*函数名)(参数列表)
int add(int a, int b) {return a + b;
}int main() {int(*fun)(int, int) = add;cout << fun(1,2) << endl;
}

函数对象

在C++中,函数对象(Function Objects)是一种类或结构体,它重载了函数调用运算符operator(),因此可以像函数一样被调用。函数对象有时也被称为仿函数(Functor)。

  1. 使用函数对象:函数对象可以像普通函数一样被调用,通过在对象后加括号并传递参数来执行操作。例如:
  2. 重载operator():函数对象需要重载operator(),并根据需要定义参数和返回值。通过重载operator(),函数对象就可以像函数一样被调用。
  3. 状态保持:与普通函数不同的是,函数对象可以包含状态。这意味着函数对象可以在其内部保持一些状态信息,并在每次调用时进行更新。这使得函数对象更加灵活且功能强大。
  4. 模板函数对象:函数对象可以是模板类,可以接受不同类型的参数。这样可以实现更通用和灵活的函数对象,适用于多种情况。
  5. 标准库中的函数对象:C++标准库提供了许多预定义的函数对象,如std::plus、std::minus、std::greater等,可以直接使用这些函数对象完成特定的操作,而不用自己定义函数对象。
  6. 使用场景:函数对象通常用于泛型编程、STL算法、排序、自定义比较函数等情况。通过函数对象,我们可以定义自己的函数行为,并将其应用于各种数据结构和算法中。
class Max
{
public:
/*
first_argument_type: 表示第一个参数的类型。
second_argument_type: 表示第二个参数的类型。
result_type: 表示返回值的类型。
*/typedef int first_argument_type;typedef int second_argument_type;typedef int result_type;int operator()(int x, int y) const {return x>y?x:y;}
};// 将函数对象作为参数传递给函数
void testMaxNum(int a, int b, Max m) {cout << m(a, b) << endl;
}int main() {Max max1;cout << max1(5,6) << endl;// test functionalcout << testMaxNum(5,6, Max());
}

functional

function 模板的参数就是函数的类型,一个函数对象放到 function 里之后,外界可以观察到的就只剩下它的参数、返回值类型和执行效果了。注意 function 对象的创建还是比较耗资源的,所以请你只在用 auto 等方法解决不了问题的时候使用这个模板。

function<返回值(参数列表)>
function<int(int,int)> add = [](int x, int y) {return x + y;};

谓词

返回bool值得函数对象,一般用于排序等操作

常见二元谓词:

  1. great_equal : 大于等于
  2. great : 大于
  3. less : 小于
  4. less_equal 小于等于
// 可以自己实现谓词
class Cmp 
{
public:bool operator()(int x, int y) {return x < y;}
};void display(vector<int>& vec) {for(auto v : vec) {cout << v << " ";}cout << endl;
}int main() {vector<int> vec({1,2,3,2,4,5,6});cout << "=== 二元谓词 === " << endl;sort(vec.begin(), vec.end(), greater<int>());display(vec);sort(vec.begin(), vec.end(),less<int>());display(vec);sort(vec.begin(), vec.end(),greater_equal<int>());display(vec);sort(vec.begin(), vec.end(), less_equal<int>());display(vec);    
}

绑定

bind1st and bind2nd

C++98函数,C++17已被bind函数替代,适应于函数对象,可以绑定指定值到函数对象的第一/二个参数

案例:

void test1() {auto fun1 = std::bind1st(plus<int>(), 5);auto fun2 = std::bind2nd(plus<int>(), 4);cout << "bind the first args: " << fun1(4) << endl;cout << "bind the second args: " << fun2(5) << endl;
}

bind模板

可以绑定任意参数数量的函数,配合占位符(std::placeholders::_x,x代表参数几)使用

案例:

void test2() {auto fun1 = std::bind(plus<int>(), std::placeholders::_1, 5);auto fun2 = std::bind(plus<int>(), 4, std::placeholders::_1);cout << "bind the first args: " << fun1(4) << endl;cout << "bind the second args: " << fun2(5) << endl;
}

测试代码:

#include <iostream>
#include <functional>
using namespace std;class Max
{
public:
/*
first_argument_type: 表示第一个参数的类型。
second_argument_type: 表示第二个参数的类型。
result_type: 表示返回值的类型。
*/typedef int first_argument_type;typedef int second_argument_type;typedef int result_type;int operator()(int x, int y) const {return x>y?x:y;}
};class RightNum
{
public:typedef int first_argument_type;typedef int second_argument_type;typedef int result_type;int operator()(int x, int y) const {return y;}
};
//int add(int x, int y) {return x + y;
} int main() {// functional objectcout << "Max(x, y) = ";Max m_max;cout << m_max(10,11) << endl;// bind1st and bind2ndcout << "===test bind1st and bind2nd===\n";auto rightFun1 = bind1st(RightNum(), 0);auto rightFun2 = bind2nd(RightNum(), 0);cout << "(0, y)->right num = ";cout << rightFun1(10) << endl;cout << "(x, 0)->right num = ";cout << rightFun2(10) << endl;// test bind// bind(fun, args...)cout << "=========test bind===============\n";cout << "functional object\n";auto rightNum3 = bind(RightNum(),std::placeholders::_1, 1);auto rightNum4 = bind(RightNum(),1, std::placeholders::_1);cout << rightNum3(10) << endl;cout << rightNum4(10) << endl;cout << "functional pointer\n";cout << "add(1, y) = ";auto adder = bind(add, 1, std::placeholders::_1);cout << adder(10) << endl;// labmdacout << "===test labmda===" << endl;int(*fun_l1)(int,int) = [](int x, int y) {return x + y;};cout << fun_l1(1,2) << endl;
}

lambda表达式

lambda表达式是C++11中引入的一项新技术,利用lambda表达式可以编写内嵌的匿名函数,用以替换独立函数或者函数对象,并且使代码更可读。

[捕获列表] (形参列表) mutable 异常列表-> 返回类型
{函数体
}
  • 捕获列表:捕获外部变量,捕获的变量可以在函数体中使用(可省略,即不捕获外部变量)

    []:默认不捕获任何变量;
    [=]:默认以值捕获所有变量;
    [&]:默认以引用捕获所有变量;
    [x]:仅以值捕获x,其它变量不捕获;
    [&x]:仅以引用捕获x,其它 变量不捕获;
    [=, &x]:默认以值捕获所有变量,但是x是例外,通过引用捕获;
    [&, x]:默认以引用捕获所有变量,但是x是例外,通过值捕获;
    [this]:通过引用捕获当前对象(其实是复制指针);
    [*this]:通过传值方式捕获当前对象;

  • 形参列表:和普通函数的形参列表一样(可省略,即无参数列表)

  • mutable:mutable 关键字,如果有,则表示在函数体中可以修改捕获变量

  • 异常列表:noexcept / throw(...),和普通函数的异常列表一样,可省略,即代表可能抛出任何类型的异常。

  • 返回类型:和函数的返回类型一样。可省略,如省略,编译器将自动推导返回类型。

  • 函数体:代码实现。可省略,但是没意义。

void testLambda() {auto f = []() {return 1;}auto f2 = [=](int x, int y) {return a+b;}
}

Lambda表达式的底层实现

Lambda的本质是函数对象(仿函数),即Lambda对象调用operator()函数。

其实现类似如下:

class Lambda_xxx
{public:Lambda_xxx(int a1, int a2, args...):args1(a1), args2(a2){};return_type operator(int a1, int a2,args...) const {/*函数体*/}
private:int args1;int args2;/*....*/
};

其中,类名 lambda_xxxx 的 xxxx 是为了防止命名冲突加上的。

  1. lambda 表达式中的捕获列表,对应 lambda_xxxx 类的 private 成员
  2. lambda 表达式中的形参列表,对应 lambda_xxxx 类成员函数 operator() 的形参列表
  3. lambda 表达式中的 mutable,表明 lambda_xxxx 类成员函数 operator() 的是否具有常属性 const,即是否是 常成员函数
  4. lambda 表达式中的返回类型,对应 lambda_xxxx 类成员函数 operator() 的返回类型
  5. lambda 表达式中的函数体,对应 lambda_xxxx 类成员函数 operator() 的函数体
    另外,lambda 表达 捕获列表的捕获方式,也影响 对应 lambda_xxxx 类的 private 成员 的类型

值捕获:private 成员的类型与捕获变量的类型一致
引用捕获:private 成员 的类型是捕获变量的引用类型

class task
{
public:task(int a, int b):m_a(a), m_b(b){}void sum() {auto res = [this]() {return this->m_a + this->m_b;}return res();}
private:int m_a;int m_b;
};//返回Lambda表达式
auto add = [](int n) {return [n](int x) {return n + x;}
}cout << add(1)(2) << endl;

Lambda函数对象与函数指针的转换

如果 lambda 表达式不捕获任何外部变量,则存在Lambda函数对象到函数指针的转换.

lambda_xxxx => 函数指针
int(*p)(int,int) = [](int a, int b) {return a + b;
}

参考:C++ 学习之函数对象_c++函数对象-CSDN博客

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

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

相关文章

【linux内核】 BCC Tools命令汇总

什么是BCC Tools 直接通过BPF指令编写BPF 程序是非常繁琐的事情,尤其对于运维人员来说,花70%的时间来编写一个BPF程序来实现一个检查点,不如花70%的时间来熟悉别人已经写好的BPF程序,并且把别人的BPF程序有效的串联起来形成自己分析问题的套路,等有了闲暇时间再去尝试突破…

20222311 2024-2025-1 《网络与系统攻防技术》实验四实验报告

1. 实验内容 1.1恶意代码文件类型标识、脱壳与字符串提取 对提供的rada恶意代码样本,进行文件类型识别,脱壳与字符串提取,以获得rada恶意代码的编写作者,具体操作如下: (1)使用文件格式和类型识别工具,给出rada恶意代码样本的文件格式、运行平台和加壳工具; (2)使用…

进销存系统怎么实施?进销存的实施流程解析

很多老板都知道,企业的日常运营中,进销存管理是至关重要的核心环节之一。它涉及到企业的采购、库存、销售等多个方面,直接影响企业的资金流动、库存控制及客户满意度。因此,如何有效实施进销存管理系统,已成为许多企业提升运营效率、降低成本、优化资源配置的重要任务。 随…

解决centos7.9镜像问题

解决方案 https://wiki.bafangwy.com/doc/719/ 安装chrome(https://www.cnblogs.com/zxqblogrecord/p/13171516.html)时出现以下问题: failure: repodata/repomd.xml from centos7: [Errno 256] No more mirrors to try. http://mirror.centos.org/centos/7/os/x86_64/repodat…

【IDEA】IntelliJ IDEA远程开发:释放本地设备,打破资源与环境的限制

IDEA的远程开发 (Remote Development) 是指在不依赖于本地计算资源的情况下,通过连接远程服务器或虚拟机来进行代码编写、调试和构建的开发模式。这种方式主要通过 JetBrains Gateway 和 JetBrains Projector 提供支持,可以在本地 IDE 界面与远程环境紧密协作,帮助开发者解决…

有哪些免费的轻量级在线 CRM 系统?6款CRM系统盘点

对于许多中小型企业和初创公司而言,如何选择一款既能满足日常需求又具性价比的CRM系统很重要。通常来说,系统的成本是企业选型过程中不可忽视的因素,但在这么多的CRM系统中,各类CRM系统的功能与价格对比真的很容易让人眼花缭乱。 本文将盘6款值得关注的免费轻量级在线CRM系…

[极客大挑战 2019]PHP 1

[极客大挑战 2019]PHP 1 打开文件发现提示信息“备份文件”,查看源码并未发现其他有效信息采用dirsearch爆破目录,找到www.zip文件解压zip,发现是源码泄露提交flag,发现不正确,看到index.php源码中有Get传参,然后反序列化函数unserialize(),判断为反序列化漏洞;查看class…

【IDEA】告别繁琐反编译:IDEA中轻松反编译与修改Jar包

本文带你了解 JarEditor 插件的强大功能,它让你无语再用其他第三方工具,也无需解压 JAR 文件就能直接在IDEA中编辑其中的类和资源文件。还能用它轻松添加、删除或重命名 JAR 包里的文件。即使遇到混淆过的 JAR,JarEditor 也能通过 Javassist 工具搞定。 支持的功能 JarEdito…

[极客大挑战 2019]BuyFlag 1

[极客大挑战 2019]BuyFlag 1 打开实例发现pay.php页面,有提示信息打开源码发现password post提交逻辑burpsuite抓包传参,传入money和password参数,这里password是==弱比较,所以加个字符就可以绕过 password=404a&money=100000000回显发现并没有变化 注意到学生需要CUIT(…

写什么代码来清空缓冲区

在 C++ 中,清空输入缓冲区的方法主要用于清除 std::cin 输入流中的残留字符,避免它们影响后续的输入操作。下面介绍几种常用的办法。 1. cin.ignore() 方法 cin.ignore() 是最常用的清空缓冲区方法,可以忽略输入流中的若干字符。cin.ignore(numeric_limits<streamsize>…

[ACTF2020 新生赛]BackupFile 1

[ACTF2020 新生赛]BackupFile 1 打开实例,显示Try to find out source file!(尝试找出源文件!)dirsearch扫描,发现flag页面以及备份文件泄露wget下载并打开index.php.bak,发现flag.php的源码这边要求参数key为数值的同时又要==123ffwsfwefwf24r2f32ir23jrw923rskfjwtsw54w…