c++函数指针 回调函数

news2024/9/22 15:47:36

 

目录

函数指针

​编辑

实例

函数指针作为某个函数的参数

实例

 std::function轻松实现回调函数

绑定一个函数

作为回调函数

 作为函数入参



函数指针

函数指针是指向函数的指针变量。

通常我们说的指针变量是指向一个整型、字符型或数组等变量,而函数指针是指向函数。

函数指针可以像一般函数一样,用于调用函数、传递参数。

函数指针类型的声明:

typedef type (*fun_ptr)(type,type); // 声明一个指向同样参数、返回值的函数指针类型

实例

以下实例声明了函数指针变量 p,指向函数 max:

#include <stdio.h>
#define  _CRT_SECURE_NO_WARNINGS
typedef int(*func_ptr)(int, int);

int max(int x, int y)
{
	return x > y ? x : y;
}

int main(void)
{
	//p是指向max的函数指针
	func_ptr p = &max;//也可以写成 func_ptr p = max;

	int a, b, c, d;

	printf("请输入三个数字:");
	scanf("%d %d %d", &a, &b, &c);

	/* 与直接调用函数等价,d = max(max(a, b), c) */
	d = p(p(a, b), c);

	printf("最大的数字是: %d\n", d);

	return 0;
}

函数指针作为某个函数的参数

函数指针变量可以作为某个函数的参数来使用的,回调函数就是一个通过函数指针调用的函数。

简单讲:回调函数是由别人的函数执行时调用你实现的函数。

以下是来自知乎作者常溪玲的解说:

你到一个商店买东西,刚好你要的东西没有货,于是你在店员那里留下了你的电话,过了几天店里有货了,店员就打了你的电话,然后你接到电话后就到店里去取了货。在这个例子里,你的电话号码就叫回调函数,你把电话留给店员就叫登记回调函数,店里后来有货了叫做触发了回调关联的事件,店员给你打电话叫做调用回调函数,你到店里去取货叫做响应回调事件。

实例

实例中 populate_array() 函数定义了三个参数,其中第三个参数是函数的指针,通过该函数来设置数组的值。

实例中我们定义了回调函数 getNextRandomValue(),它返回一个随机值,它作为一个函数指针传递给 populate_array() 函数。

populate_array() 将调用 10 次回调函数,并将回调函数的返回值赋值给数组。


#include <stdlib.h>
#include <stdio.h>

void populate_array(int* array, size_t arraySize, int (*getNextValue)(void))
{
	for (size_t i = 0; i < arraySize; i++)
		array[i] = getNextValue();
}

// 获取随机值
int getNextRandomValue(void)
{
	return rand();
}

int main(void)
{
	int myarray[10];

	populate_array(myarray, 10, getNextRandomValue);//函数的名称就是函数的地址
	for (int i = 0; i < 10; i++) {
		printf("%d ", myarray[i]);
	}
	printf("\n");
	return 0;
}

 std::function轻松实现回调函数

#include <functional>
#include <iostream>
 
struct Foo
{
    Foo(int num) : num_(num) {}
    void print_add(int i) const { std::cout << num_ + i << '\n'; }
    int num_;
};
 
void print_num(int i)
{
    std::cout << i << '\n';
}
 
struct PrintNum
{
    void operator()(int i) const
    {
        std::cout << i << '\n';
    }
};
 
int main()
{
    // store a free function
    std::function<void(int)> f_display = print_num;
    f_display(-9);
 
    // store a lambda
    std::function<void()> f_display_42 = []() { print_num(42); };
    f_display_42();
 
    // store the result of a call to std::bind
    std::function<void()> f_display_31337 = std::bind(print_num, 31337);
    f_display_31337();
 
    // store a call to a member function
    std::function<void(const Foo&, int)> f_add_display = &Foo::print_add;
    const Foo foo(314159);
    f_add_display(foo, 1);
    f_add_display(314159, 1);
 
    // store a call to a data member accessor
    std::function<int(Foo const&)> f_num = &Foo::num_;
    std::cout << "num_: " << f_num(foo) << '\n';
 
    // store a call to a member function and object
    using std::placeholders::_1;
    std::function<void(int)> f_add_display2 = std::bind(&Foo::print_add, foo, _1);
    f_add_display2(2);
 
    // store a call to a member function and object ptr
    std::function<void(int)> f_add_display3 = std::bind(&Foo::print_add, &foo, _1);
    f_add_display3(3);
 
    // store a call to a function object
    std::function<void(int)> f_display_obj = PrintNum();
    f_display_obj(18);
 
    auto factorial = [](int n)
    {
        // store a lambda object to emulate "recursive lambda"; aware of extra overhead
        std::function<int(int)> fac = [&](int n) { return (n < 2) ? 1 : n * fac(n - 1); };
        // note that "auto fac = [&](int n) {...};" does not work in recursive calls
        return fac(n);
    };
    for (int i{5}; i != 8; ++i)
        std::cout << i << "! = " << factorial(i) << ";  ";
    std::cout << '\n';
}

绑定一个函数

#include <functional>
#include <iostream>

//普通函数
void func(void)
{
	std::cout << __FUNCTION__ << std::endl;
}
//静态类成员函数
class Foo
{
public:
	static int foo_func(int a)
	{
		std::cout << __FUNCTION__ << "(" << a << ") ->:";
		return a;
	}
};


int main(void)
{
	std::function<void(void)> fr = func;
	fr();
	std::function<int(int)> fr1 = Foo::foo_func;
	std::cout << fr1(456) << std::endl;
}

作为回调函数

#include <functional>
#include <iostream>
class A
{
std::function<void()> callback_;
public:
  A(const std::function<void()>& f) :callback_(f) {};
void notify(void)
{
    callback_();
  }
};
class Foo {
public:
void operator()(void)
{
std::cout << __FUNCTION__ << std::endl;
  }
};
int main(void)
{
  Foo foo;
A aa(foo);
  aa.notify();
}

 作为函数入参

#include <functional>
#include <iostream>
void call_when_even(int x, const std::function<void(int)>& f)
{
if (!(x & 1))
  {
    f(x);
  }
}
void output(int x)
{
std::cout << x << " ";
}
int main(void)
{
for (int i = 0; i < 10; ++i)
  {
    call_when_even(i, output);
  }
std::cout << std::endl;
}

一些AI给出的回答

`std::function`是C++标准库中的一个通用、可调用、多态的函数封装器,可以用来存储任何可调用对象的引用,如函数指针、函数对象、Lambda表达式等。它的主要作用是将函数作为参数传递,实现回调函数的功能。

下面是一个简单的实例,演示了如何使用`std::function`存储一个函数,并调用它:` 

 

在这个例子中,我们定义了

#include <iostream>
#include <functional>

void print_hello() {
    std::cout << "Hello, world!" << std::endl;
}

int main() {
    std::function<void()> func = print_hello;
    func();
    return 0;
}

一个名为`print_hello`的函数,用于打印"Hello, world!"。然后,我们创建了一个`std::function<void()>`类型的变量`func`,并将`print_hello`函数的地址赋值给`func`。最后,我们调用`func`,输出"Hello, world!"。

在C++中,`std::function`是一个通用、可调用、多态的函数封装器,可以用来存储任何可调用对象的引用,如函数指针、函数对象、Lambda表达式等。`std::function`的模板参数`T`表示存储的函数的参数类型,当`T`是一个函数指针类型时,`std::function`可以用来存储一个函数指针;当`T`是一个函数对象类型时,`std::function`可以用来存储一个函数对象;当`T`是一个Lambda表达式类型时,`std::function`可以用来存储一个Lambda表达式。

下面是一些例子:

// 存储一个接受两个int参数的函数
std::function<int(int, int)> func1;

// 存储一个接受一个int参数并返回一个int的函数
std::function<int(int)> func2;

// 存储一个不接受任何参数的函数
std::function<void()> func3;

// 存储一个Lambda表达式
std::function<int(int)> func4 = [](int x) { return x * 2; };

在这些例子中,`func1`和`func2`可以用来存储任何接受相应参数类型的函数,`func3`可以用来存储任何不接受任何参数的函数,`func4`可以用来存储一个接受一个int参数并返回一个int的Lambda表达式。


 

#include <iostream>
#include <functional>

class A {
public:
    A(const std::function<void()>& f) :callback_(f) {};

    void print() {
        callback_();
    }

private:
    std::function<void()> callback_;
};

int main() {
    A obj([&]() {
        std::cout << "Hello, World!" << std::endl;
    });

    obj.print();

    return 0;
}

在这个例子中,我们定义了一个名为A的类,它有一个构造函数,接受一个名为callback_的std::function<void()>类型的参数。我们还定义了一个名为print的方法,用于调用callback_函数。在main函数中,我们创建了一个A类的对象,并将一个Lambda表达式作为其构造函数的参数传递。然后我们调用obj的print方法,输出"Hello, World!"

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

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

相关文章

基于SpringBoot的综合小区管理系统的设计与实现

文章目录 项目介绍主要功能截图&#xff1a;部分代码展示设计总结项目获取方式 &#x1f345; 作者主页&#xff1a;超级无敌暴龙战士塔塔开 &#x1f345; 简介&#xff1a;Java领域优质创作者&#x1f3c6;、 简历模板、学习资料、面试题库【关注我&#xff0c;都给你】 &…

【前端素材】推荐优质后台管理系统 Greeva平台模板(附源码)

一、需求分析 1、系统定义 后台管理系统是一种用于管理网站、应用程序或系统的管理界面&#xff0c;通常由管理员和工作人员使用。它提供了访问和控制网站或应用程序后台功能的工具和界面&#xff0c;使其能够管理用户、内容、数据和其他各种功能。 2、功能需求 后台管理系…

vulhub中JBoss 5.x/6.x 反序列化漏洞复现(CVE-2017-12149)

该漏洞为 Java反序列化错误类型&#xff0c;存在于 Jboss 的 HttpInvoker 组件中的 ReadOnlyAccessFilter 过滤器中。该过滤器在没有进行任何安全检查的情况下尝试将来自客户端的数据流进行反序列化&#xff0c;从而导致了漏洞。 漏洞复现 利用攻击进行漏洞利用yunxu1/jboss-_…

xsslabs第七关

源码 <!DOCTYPE html><!--STATUS OK--><html> <head> <meta http-equiv"content-type" content"text/html;charsetutf-8"> <script> window.alert function() { confirm("完成的不错&#xff01;"…

DSP软件架构

&#x1f3ac;个人简介&#xff1a;一个全栈工程师的升级之路&#xff01; &#x1f4cb;个人专栏&#xff1a;计算机杂记 &#x1f380;CSDN主页 发狂的小花 &#x1f304;人生秘诀&#xff1a;学习的本质就是极致重复! 目录 一 数字信号处理基本运算 二 DSP软件架构 1 哈…

nginx如何配置命令启动

我安装好nginx后&#xff0c;发现不能使用systemctl start nginx或者systemctl stop nginx来控制启停 解决方法如下 首先要建一个nginx.pid的文件 一般是建在 /var/run/这个路径下面 sudo touch /var/run/nginx.pid 添加权限 sudo chmod 644 /var/run/nginx.pid可以进入到…

136.乐理基础-旋律音程、和声音程、自然音程、变化音程

内存参考于&#xff1a;三分钟音乐社 上一个内容&#xff1a;135.乐理基础-半音是小二度吗&#xff1f;全音是大二度吗&#xff1f;三全音-CSDN博客 上一个内容里练习的答案&#xff1a; 旋律音程 旋律音程指的是音程中两个音&#xff0c;一前一后&#xff0c;有先后顺序依次…

Linux调试器-gdb使用与冯诺依曼体系结构

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言 Linux调试器-gdb使用 1. 背景 2. 开始使用 冯诺依曼体系结构 总结 前言 世上有两种耀眼的光芒&#xff0c;一种是正在升起的太阳&#xff0c;一种是正在努力学…

java009 - Java调试debugger

1、debugger概述 程序的调试工具&#xff0c;用于查看追踪程序的执行流程&#xff0c;也可以调试程序。 2、debugger调试流程 2.1 如何加断点 2.2 如何运行加了断点的程序 在代码区域右键---->debugger执行 2.3 看哪里 看console窗口 2.4 点哪里 点step into(F7)这个箭…

SQL Server 阻止了对组件 ‘Ole Automation Procedures‘ 的 过程‘sys.sp_OACreate‘ 的访问

SQL Server 阻止了对组件 Ole Automation Procedures 的 过程sys.sp_OACreate 的访问&#xff0c;因为此组件已作为此服务器安全配置的一部分而被关闭。系统管理员可以通过使用 sp_configure 启用 Ole Automation Procedures。有关启用 Ole Automation Procedures 的详细信息&a…

数据结构从入门到精通——算法的时间复杂度和空间复杂度

算法的时间复杂度和空间复杂度 前言一、算法效率1.1 如何衡量一个算法的好坏1.2 算法的复杂度 二、时间复杂度2.1 时间复杂度的概念2.2 大O的渐进表示法2.3常见时间复杂度计算举例2.4等差数列计算公式2.5等比数列计算方法 三、空间复杂度四、 常见复杂度对比五、 复杂度的oj练习…

灯塔:HTML笔记

网页由哪些部分组成&#xff1f; *文字 图片 音频 视频 超链接 程序员写的代码是通过浏览器转换成网页的 五大浏览器有哪些&#xff1f; *IE浏览器 *火狐浏览器&#xff08;Firefox&#xff09; *谷歌浏览器&#xff08;Chrome&#xff09; *Safari浏览器 *欧朋浏览器&…

【C++】数组、函数、指针

文章目录 1.数组1.1一维数组1.2二维数组 2.函数3.指针&#xff1a;可以通过指针间接访问内存(指针记录地址&#xff09;3.1 指针的定义和使用3.2 指针所占用空间3.3 空指针和野指针3.4 const修饰指针3.5指针和数组3.6指针和函数3.7练习&#xff08;指针、数组、函数&#xff09…

【Java设计模式】三、

文章目录 0、案例&#xff1a;咖啡屋1、简单工厂模式 静态工厂&#xff08;不属于23种之列&#xff09;2、工厂方法模式3、抽象工厂模式4、建造者模式5、原型设计模式 0、案例&#xff1a;咖啡屋 模拟咖啡店点餐。咖啡有多种&#xff0c;抽象类&#xff0c;子类为各种咖啡。咖…

第四个程序:(a+b)^2+a/c-c^2

&#xff08;ab&#xff09;^2a/c-c^2运算出结果 步骤&#xff1a; 第一步&#xff1a; 建立项目; 第二步&#xff1a;添加部件&#xff0c;连线&#xff0c;完成程序 第三步&#xff1a;运行得出结果 视频&#xff1a; (ab)^2ac-c^2

软考-计算题

1.二维矩阵转换成一维矩阵 2.算术表达式&#xff1a; 3.计算完成项目的最少时间&#xff1a;之前和的max&#xff08;必须之前的所有环节都完成&#xff09; 松弛时间&#xff1a;最晚开始时间-最早开始时间 最早&#xff1a;之前环节都完成的和的max 最晚&#xff1a;总时间…

2024年经典【自动化面试题】附答案

一、请描述一下自动化测试流程&#xff1f; 自动化测试流程一般可以分为以下七步&#xff1a; 编写自动化测试计划&#xff1b; 设计自动化测试用例&#xff1b; 编写自动化测试框架和脚本&#xff1b; 调试并维护脚本&#xff1b; 无人值守测试&#xff1b; 后期脚本维…

java009 - Java面向对象基础

1、类和对象 1.1 什么是对象 万物皆对象&#xff0c;客观存在的事物皆为对象。 1.2 什么是面向对象 1.3 什么是类 类是对现实生活中一类具有共同属性和行为的事物抽象。 特点&#xff1a; 类是对象的数据类型类是具有相同属性和行为的一组对象的集合 1.4 什么是对象的属…

装饰器模式 详解 设计模式

装饰器模式 它允许你在不改变对象结构的情况下&#xff0c;动态地将新功能附加到对象上。 结构&#xff1a; 抽象组件&#xff08;Component&#xff09;&#xff1a;定义了原始对象和装饰器对象的公共接口或抽象类&#xff0c;可以是具体组件类的父类或接口。具体组件&…

【c++】stack和queue模拟实现

> 作者简介&#xff1a;დ旧言~&#xff0c;目前大二&#xff0c;现在学习Java&#xff0c;c&#xff0c;c&#xff0c;Python等 > 座右铭&#xff1a;松树千年终是朽&#xff0c;槿花一日自为荣。 > 目标&#xff1a;能手撕stack和queue模拟 > 毒鸡汤&#xff1a;…