精华内容
下载资源
问答
  • 回调函数作用

    2014-08-04 22:37:44
    本文将讨论函数指针的基本原则并说明如何使用函数指针实现回调。注意这里针对的是普通的函数,不包括完全依赖于不同语法和语义规则的类成员函数(类成员指针将在另文中讨论)
  • 关于回调函数中变量作用域的讨论精品推荐,大家可以参考下。
  • 主要介绍了PHP将回调函数作用到给定数组单元的方法,是十分重要的一种应用,需要的朋友可以参考下
  • Java 回调函数作用和使用场景

    千次阅读 2017-09-13 13:51:03
    1. 什么是回调函数  回调函数(callback Function),顾名思义,用于回调的函数。 回调函数只是一个功能片段,由用户按照回调函数调用约定来实现的一个函数。回调函数是一个工作流的一部分,由工作流来决定函数的...
    1. 什么是回调函数 
    
    
    回调函数(callback Function),顾名思义,用于回调的函数。 回调函数只是一个功能片段,由用户按照回调函数调用约定来实现的一个函数。回调函数是一个工作流的一部分,由工作流来决定函数的调用(回调)时机。回调函数包含下面几个特性: 
    1、属于工作流的一个部分;
    2、必须按照工作流指定的调用约定来申明(定义);
    3、他的调用时机由工作流决定,回调函数的实现者不能直接调用回调函数来实现工作流的功能; 

    2. 回调机制

    回调机制是一种常见的设计模型,他把工作流内的某个功能,按照约定的接口暴露给外部使用者,为外部使用者提供数据,或要求外部使用者提供数据。

    3、使用场景

    (1)在必须给别的函数提供接口的时候</p>
    (2) 在需要定时操作,或者条件操作的时候

    4、回调方法测试用例:

    (1)多线程的Runnable就属于一个回调接口,也就是别的类定义规则,你实现传入,由别人调用你的方法罢了 。

    [java]  view plain  copy
    1. package com.gy.appsdk.dao;  
    2.   
    3. /** 
    4.  * 定义一套标准方法接口 
    5.  * @author Administrator 
    6.  * 
    7.  */  
    8. interface WindowListener{  
    9.     public void open();  
    10.       
    11. }  
    12.   
    13.   
    14. public class Window {  
    15.       
    16.     private WindowListener windowListener;  
    17.       
    18.     public void registerWindowListener(WindowListener windowListener){  
    19.         this.windowListener = windowListener;  
    20.     }  
    21.       
    22.     public void show(){  
    23.         if(windowListener!=null){  
    24.             windowListener.open();  
    25.         }  
    26.     }  
    27. }  
    28.   
    29. /*      //使用方式如下: 
    30.  *      Window window = new Window(); 
    31.          
    32.         //多线程的Runnable就属于一个回调接口,也就是别的类定义规则,你实现传入,由别人调用你的方法罢了  
    33.           
    34.         window.registerWindowListener(new WindowListener() { 
    35.             @Override 
    36.             public void open() { 
    37.                 System.out.println("打开"); 
    38.             } 
    39.         }); 
    40.          
    41.         window.show(); 
    42. */  


    (2)熟悉MS-Windows和X Windows事件驱动设计模式的开发人员,通常是把一个方法的指针传递给事件源,当某一事件发生时来调用这个方法(也称为“回调”)。Java的面向对象的模型目前不支持方法指针,似乎不能使用这种方便的机制。

    Java支持interface,通过interface可以实现相同的回调。其诀窍就在于定义一个简单的interface,申明一个被希望回调的方法。


    例如,假定当某一事件发生时会得到通知,我们可以定义一个interface:

    [java]  view plain  copy
    1. public interface InterestingEvent {  
    2.     // 这只是一个普通的方法,可以接收参数、也可以返回值  
    3.     public void interestingEvent();  
    4. }  

    这样我们就有了任何一个实现了这个接口类对象的手柄grip。

    当一事件发生时,需要通知实现 InterestingEvent  接口的对象,并调用 interestingEvent()  方法。
    [java]  view plain  copy
    1. class EventNotifier {  
    2.     private InterestingEvent ie;  
    3.     private boolean somethingHappened;  
    4.   
    5.     public EventNotifier(InterestingEvent event) {  
    6.         ie = event;  
    7.         somethingHappened = false;  
    8.     }  
    9.   
    10.     public void doWork() {  
    11.         if (somethingHappened) {  
    12.             // 事件发生时,通过调用接口的这个方法来通知  
    13.             ie.interestingEvent();  
    14.         }          
    15.     }  
    16. }  

    在这个例子中,用 somethingHappened  来标志事件是否发生。

    希望接收事件通知的类必须要实现 InterestingEvent  接口,而且要把自己的引用传递给事件的通知者。
    [java]  view plain  copy
    1. public class CallMe implements InterestingEvent {  
    2.     private EventNotifier en;  
    3.   
    4.     public CallMe() {  
    5.         // 新建一个事件通知者对象,并把自己传递给它  
    6.         en = new EventNotifier(this);  
    7.     }  
    8.   
    9.     // 实现事件发生时,实际处理事件的方法  
    10.     public void interestingEvent() {  
    11.         // 这个事件发生了,进行处理  
    12.     }  
    13. }  

    以上是通过一个非常简单的例子来说明Java中的回调的实现。

    当然,也可以在事件管理或事件通知者类中,通过注册的方式来注册多个对此事件感兴趣的对象。

    1. 定义一个接口InterestingEvent ,回调方法nterestingEvent(String event) 简单接收一个String 参数。

    [java]  view plain  copy
    1. interface InterestingEvent {  
    2.     public void interestingEvent(String event);  
    3. }  

    2. 实现InterestingEvent接口,事件处理类

    [java]  view plain  copy
    1. class CallMe implements InterestingEvent {  
    2.     private String name;  
    3.     public CallMe(String name){  
    4.         this.name = name;  
    5.     }      
    6.     public void interestingEvent(String event) {  
    7.         System.out.println(name + ":[" +event  + "] happened");  
    8.     }  
    9. }  

    3. 事件管理者,或事件通知者

    [java]  view plain  copy
    1. class EventNotifier {  
    2.     private List<CallMe> callMes = new ArrayList<CallMe>();  
    3.       
    4.     public void regist(CallMe callMe){  
    5.         callMes.add(callMe);  
    6.     }  
    7.       
    8.     public void doWork(){  
    9.         for(CallMe callMe: callMes) {  
    10.             callMe.interestingEvent("sample event");  
    11.         }  
    12.     }      
    13. }  

    4. 测试

    [java]  view plain  copy
    1. public class CallMeTest {  
    2.     public static void main(String[] args) {  
    3.         EventNotifier ren = new EventNotifier();  
    4.         CallMe a = new CallMe("CallMe A");  
    5.         CallMe b = new CallMe("CallMe B");  
    6.   
    7.         // regiest  
    8.         ren.regist(a);  
    9.         ren.regist(b);  
    10.           
    11.         // test  
    12.         ren.doWork();          
    13.     }  
    14. }  
    展开全文
  • 本篇文章主要介绍了回调函数的意义以及python实现实例,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
  • 主要介绍了Python回调函数用法,以实例形式较为详细的分析了Python回调函数的定义、功能及相关使用技巧,需要的朋友可以参考下
  • 回调函数

    千次阅读 多人点赞 2019-09-09 17:36:28
    回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数回调函数不是由该函数的实现方直接调用,而是在特定...

    回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。

    其实回调就是一种利用函数指针进行函数调用的过程.

    为什么要用回调呢?比如我要写一个子模块给你用,来接收远程socket发来的命令.当我接收到命令后,需要调用你的主模块的函数, 来进行相应的处理.但是我不知道你要用哪个函数来处理这个命令,我也不知道你的主模块是什么.cpp或者.h,或者说,我根本不用关心你在主模块里怎么处理它,也不应该关心用什么函数处理它......怎么办呢?使用回调!

    —— lone wolf

    使用回调函数实际上就是在调用某个函数(通常是API函数)时,将自己的一个函数(这个函数为回调函数)的地址作为参数传递给那个函数。而那个函数在需要的时候,利用传递的地址调用回调函数,这时你可以利用这个机会在回调函数中处理消息或完成一定的操作。

    —— 某专家

    回调函数,就是由你自己写的。你需要调用另外一个函数,而这个函数的其中一个参数,就是你的这个回调函数名。这样,系统在必要的时候,就会调用你写的回调函数,这样你就可以在回调函数里完成你要做的事。

    —— 绿叶

    回调函数是应用程序提供给Windows系统DLL或其它DLL调用的函数,一般用于截获消息、获取系统信息或处理异步事件。应用程序把回调函数的地址指针告诉DLL,而DLL在适当的时候会调用该函数。回调函数必须遵守事先规定好的参数格式和传递方式,否则DLL一调用它就会引起程序或系统的崩溃。通常情况下,回调函数采用标准WindowsAPI的调用方式,即__stdcall,当然,DLL编制者可以自己定义调用方式,但客户程序也必须遵守相同的规定。在__stdcall方式下,函数的参数按从右到左的顺序压入堆栈,除了明确指明是指针或引用外,参数都按值传递,函数返回之前自己负责把参数从堆栈中弹出。

    —— jufengfeng

    看了这么多的资料,我只将每位的定义总结一下就一句话:使用回调函数实际上就是在调用某个函数(通常是API函数)时,将自己写的一个函数(这个函数就是回调函数)的地址作为参数传递给那个函数。回调其实就是提供使用某模块的一种方法。回调函数就好比是一个中断处理函数。

     

    3. 回调函数的作用?应该在什么情况下使用?

    用过STL的人都知道,在STL中众多算法和程序都用到回调函数,这实现了一种策略。只要任何符合我的标准的函数和计算都可以用我这个公式。你可以实现各种各样的回调函数,只要符合我的格式就能用。就上面的程序来说,你只要函数格式符合cllback第二个参数的格式不论你给别人做饭、铺床叠被都可以正常工作。这就是回调的作用,把回调实现留给别人。这是一个用法。
    有一位朋友用分层的概念来解释了回调机制:callback函数为B层,main函数和print函数为A层,A层调用了B层的回调函数callmeback,而B层的回调函数调用了A层的实现函数print。说白了B层就是一个接口。

    有一位朋友用分层的概念来解释了回调机制:callback函数为B层,main函数和print函数为A层,A层调用了B层的回调函数callmeback,而B层的回调函数调用了A层的实现函数print。说白了B层就是一个接口。
    最后要注意的是:回调函数要么是全局函数,要么是静态函数!

    
    #include <stdio.h>
    //回调函数
    int ADD(int (*callback)(int,int), int a, int b){
    	return (*callback)(a,b);//此处回调add函数...
    }
    //普通函数
    int add(int a, int b){
    	return a + b;
    }
     
    int main(void){
    	printf("%d\n",add(1,2));
    	printf("%d\n",ADD(add,1,2));
    	return 0;
    }

     

    C回调函数

    C++回调函数扩展自C回调函数,要想理解C++回调函数,先要理解C回调函数。我们通过一个实例来讲解C回调函数的使用方法。

    //callbackTest.c
    //1.定义函数onHeight(回调函数)
    //@onHeight 函数名
    //@height   参数
    //@contex   上下文
    void onHeight(double height, void* contex)
    {
    	sprint("current height is %lf",height);
    }
    
    //2.定义onHeight函数的原型
    //@CallbackFun 指向函数的指针类型
    //@height      回调参数,当有多个参数时,可以定义一个结构体
    //@contex      回调上下文,在C中一般传入nullptr,在C++中可传入对象指针
    typedef void (*CallbackFun)(double height, void* contex);
    
    //3.定义注册回调函数
    //@registHeightCallback 注册函数名
    //@callback             回调函数原型
    //@contex               回调上下文
    void registHeightCallback(CallbackFun callback, void* contex)
    {
    	double h=100;
    	callback(h,nullptr);
    }
    
    //4.main函数
    void main()
    {
    	//注册onHeight函数,即通过registHeightCallback的参数将onHeight函数指针
    	//传入给registHeightCallback函数,在registHeightCallback函数中调用
    	//callback就相当于调用onHeight函数。
    	registHeightCallback(onHeight,nullptr);
    }

    程序的运行结果是:
    current height is 100
    很多时候,注册的时候并不调用回调函数,而是在其他函数中调用,那我们可以定义一个CallbackFun全局指针变量,在注册的时候将函数指针赋给它,在要调用的调用它。如

    //定义全局指针变量
    CallbackFun* m_pCallback;
    //定义注册回调函数
    void registHeightCallback(CallbackFun callback, void* contex)
    {
    	m_pCallback = callback;
    }
    //定义调用函数
    void printHeightFun(double height)
    {
    	m_pCallback(height,nullptr);
    }
    //main函数
    void main()
    {
    	//注册回调函数onHeight
    	registHeightCallback(onHeight,nullptr);
    	//打印height
    	double h=99;
    	printHeightFun(99);
    }

     程序的运行结果是:
    current height is 99

    C++回调函数

    C++回调函数扩展自C,与C略有不同的是,C++可以使用全局函数和静态函数作为回调函数。考虑到全局函数会破坏封装性,所以一般都用静态成员函数。故除了理解函数指针,还要理解静态成员函数,具体一点是在静态成员函数中访问非静态成员函数的方法,因为我们很可能需要获取静态成员函数中的数据。

    使用场景描述

    比如说你使用了别人提供的sdk,这个sdk可能来自供应商,也有可能来自你的同事,他就提供给你一个注册回调函接口,比如就下面这个,你可以通过回调函数获取到height(某种传感器的实时返回的数据),你要怎么做?

    C++回调函数定义

    //CallbackFun类型
    //@CallbackFun 指向函数的指针类型
    //@height      回调参数,当有多个参数时,可以定义一个结构体
    //@contex      回调上下文,在C中一般传入nullptr,在C++中可传入对象指针
    typedef void (*CallbackFun)(double height, void* contex);
    
    //注册回调函数接口
    //@registHeightCallback 注册函数名
    //@callback             回调函数原型
    //@contex               回调上下文
    void registHeightCallback(CallbackFun callback, void* contex)

    首先,你要定义一个静态成员函数并注册。

    //sensorTest.cpp
    //接收数据类class Sensor
    class Sensor{
    public:
    	Sensor(){}
    	~Sensor(){}
    	//定义回调函数onHeight
    	static void onHeight(double height, void* contex)
    	{
    		cout << "current height is  " << height << endl;
    	}
    	//定义注册回调函数
    	void registCallback()
    	{
    		registHeightCallback(onHeight, this);
    	}
    }
    
    //main 函数
    void main()
    {
    	Sensor sens;
    	sens.registCallback();
    }

    运行程序,我们发现控制台一直在打印
    current height is **
    说明我们的回调函数正确实现了。到这一步不难,只要掌握基本的回调函数概念都能实现。
    现在我们有这样一种情况,我们有另外一个类,要在这个类里面实时打印获取的数据,要怎么做呢?

    静态成员函数访问非静态成员函数的方法

    我们知道静态成员函数中是只能出现静态变量和静态函数的,但是有些时候真的需要访问非静态成员函数或变量,比如我上面说的那种情况。让我们先来实现对同一个类中的非静态成员函数的访问。
    修改class Sensor如下

    //接收数据类class Sensor
    class Sensor{
    public:
    	Sensor(){}
    	~Sensor(){}
    	//定义回调函数onHeight
    	static void onHeight(double height, void* contex)
    	{
    		//cout << "current height is  " << height << endl;
    		Sensor* sen = (Sensor*)contex;
    		if(sen)  //注意判断sen是否有效
    			sen->getHeight(height);
    	}
    	//定义注册回调函数
    	void registCallback()
    	{
    		registHeightCallback(onHeight, this);
    	}
    	//新增的成员函数
    	void getHeight(double height)
    	{
    		cout << "current height is  " << height << endl;
    	}
    }

    如此修改之后,得到与修改前一样的效果(实时打印height),关键点在于注册回调函数的时候将Sensor对象的指针传给了contex,在回调函数中又将contex转换为Sensor对象指针,所以能调用普通函数。
    同理,如果注册时传入某一个对象的指针,就可以在回调函数中对该对象进行操作,这就是我们可以在一个对象中回调另一个对象的思想。

    回调对象

    现在开始解决之前提出的问题,本质是不变的,回调是指针传递,可以是函数指针,也可以是对象指针。

    //先定义一个类class DataPrint
    //打印数据类class DataPrint
    class DataPrint{
    public:
    	DataPrint(){}
    	~DataPrint(){}
    	void printHeight(double height)
    	{
    		cout << "print height is " << height << endl;
    	}
    }
    
    //要在类Sensor中增加DataPrint的指针和一个DataPrint指针赋值函数,class Sensor修改为
    //接收数据类class Sensor
    class Sensor{
    public:
    	Sensor(){}
    	~Sensor(){}
    	//定义回调函数onHeight
    	static void onHeight(double height, void* contex)
    	{
    		DataPrint* dp = (DataPrint*)contex;
    		if(dp)  //注意判断dp是否有效
    			dp->printHeight(height);
    	}
    	//定义注册回调函数
    	void registCallback()
    	{
    		registHeightCallback(onHeight, m_pDataPrint );
    	}
    	//新增的成员函数
    	void getHeight(double height)
    	{
    		//cout << "current height is  " << height << endl;
    	}
    	void setDataPrint(DataPrint* dp)
    	{
    		m_pDataPrint = dp;
    	}
    private:
    	DataPrint* m_pDataPrint;
    }
    
    //main主函数
    void main()
    {
    	DataPrint* dp=new DataPrint();
    	Sensor* sens=new Sensor();
    	//注意这两句的顺序不能颠倒
    	sens->setDataPrint(dp);
    	sens->registCallback();
    }

     这样就能实现在另一个类中取得回调函数的数据,如果无法保证DataPrint的实例化一定在Sensor之前,我们可以这样做

    //先定义一个类class DataPrint
    //打印数据类class DataPrint
    class DataPrint{
    public:
    	DataPrint(){}
    	~DataPrint(){}
    	void printHeight(double height)
    	{
    		cout << "print height is " << height << endl;
    	}
    }
    
    //要在类Sensor中增加DataPrint的指针和一个DataPrint指针赋值函数,class Sensor修改为
    //接收数据类class Sensor
    class Sensor{
    public:
    	Sensor(){}
    	~Sensor(){}
    	//定义回调函数onHeight
    	static void onHeight(double height, void* contex)
    	{
    		Sensor* sen= (Sensor*)contex;
    		if(sen)  //注意判断sen是否有效
    			sen->getHeight(height);
    	}
    	//定义注册回调函数
    	void registCallback()
    	{
    		registHeightCallback(onHeight, m_pDataPrint );
    	}
    	//新增的成员函数
    	void getHeight(double height)
    	{
    		if(m_pDataPrint )
    			m_pDataPrint ->printHeight(height);
    	}
    	void setDataPrint(DataPrint* dp)
    	{
    		m_pDataPrint = dp;
    	}
    private:
    	DataPrint* m_pDataPrint;
    }
    
    //main主函数
    void main()
    {
    	DataPrint* dp=new DataPrint();
    	Sensor* sens=new Sensor();
    	//注意这两句的顺序可以颠倒
    	sens->setDataPrint(dp);
    	sens->registCallback();
    }

    两个的区别是一个直接注册指定类的对象指针,另一个注册当前类的对象指针,间接调用另一个类的对象指针。

    更复杂的讨论

    刚才讨论的问题稍微复杂一点了,不过应该也容易理解,但是我们在实际项目中遇到的情况可能比这个复杂。比如在有层次的软件工程中,回调函数在底层,显示数据的类在上层,我们要如何把底层的数据显示到上层去?容易想到的是上层调用底层,如开个timer刷新,但这是不对的,你无法做到实时调用,你需要的是一个异步的机制,即底层一发生上层就能接收到。
    怎么做呢?还是一样的道理,把上层的类的对象指针传给底层,这时底层需要包含上层的头文件,但这是不对的,上层已经包含了底层的头文件,它们不能互相包含,如何解决这个问题?那就要用到C++继承的特性,首先在底层定义一个基类,然后在上层继承它,在上层实例化这个继承类后,将其指针设置给底层,底层对该指针的操作就是对继承类对象的操作,以此实现数据的传递。

    参考:

    [1] https://www.jianshu.com/p/26784d962f58

    [2] https://blog.csdn.net/weixin_40237626/article/details/82801409

    [3] https://blog.csdn.net/sinat_38183777/article/details/83958887

    [4] https://blog.csdn.net/yidu_fanchen/article/details/80513359

    展开全文
  • delphi 回调函数示例

    2012-05-31 11:00:51
    delphi 回调函数示例,有分直接下。
  • C语言中的回调函数

    2020-12-22 15:21:25
     回调函数的概念和作用  回调函数是程序中一种常用的函数调用方式。其特点是可以在下层软件模块(宏观上的被调用者)中,调用来自上层的软件模块(宏观上的调用者)中的函数,这和通常的调用顺序是相反的。  ...
  • STM32CUBE配置GPIO中断,以及中断回调函数编写 .
  • 回调函数作用

    万次阅读 多人点赞 2017-03-12 09:02:09
    一直不太理解回调函数作用,下面是找到的一些关于回调函数作用的解答。 1.回调函数是一个很有用,也很重要的概念。当发生某种事件时,系统或其他函数将会自动调用你定义的一段函数。 2.回调函数就相当于一个...
    回调函数的作用   原文地址: http://wmnmtm.blog.163.com/blog/static/3824571420105484116877/


    一直不太理解回调函数的作用,下面是找到的一些关于回调函数的作用的解答。

    1.回调函数是一个很有用,也很重要的概念。当发生某种事件时,系统或其他函数将会自动调用你定义的一段函数。

    2.回调函数就相当于一个中断处理函数,由系统在符合你设定的条件时自动调用。为此,你需要做三件事:1,声明;2,定义;3,设置触发条件,就是在你的函数中把你的回调函数名称转化为地址作为一个参数,以便于系统调用。

    3.所谓回调函数就是按照一定的形式由你定义并编写实现内容,当发生某种事件时,而由系统或其它函数来调用的函数。使用回调函数实际上就是在调用某个函数时,将自己编写的一个函数的地址作为参数传递给那个函数。而那个函数在需要的时候,也就是某种事情发生的时候,利用传递的函数地址调用回调函数,这时你可以利用这个机会在回调函数中处理消息或完成一定的操作。回调函数只能是全局函数,或者是静态函数,因为这个函数只是在这个类中使用,所以为了维护类的完整性,我们用类的静态成员函数来做回调函数。

    4.对于很多初学者来说,往往觉得回调函数很神秘,很想知道回调函数的工作原理。本文将要解释什么是回调函数、它们有什么好处、为什么要使用它们等等问题,在开始之前,假设你已经熟知了函数指针。

    (1)什么是回调函数?
      
    简而言之,回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用为调用它所指向的函数时,我们就说这是回调函数。

    (2)为什么要使用回调函数?
      
    因为可以把调用者与被调用者分开。调用者不关心谁是被调用者,所有它需知道的,只是存在一个具有某种特定原型、某些限制条件(如返回值为int)的被调用函数。
      
    如果想知道回调函数在实际中有什么作用,先假设有这样一种情况,我们要编写一个库,它提供了某些排序算法的实现,如冒泡排序、快速排序、shell排序、shake排序等等,但为使库更加通用,不想在函数中嵌入排序逻辑,而让使用者来实现相应的逻辑;或者,想让库可用于多种数据类型(int、float、string),此时,该怎么办呢?可以使用函数指针,并进行回调。
      
    回调可用于通知机制,例如,有时要在程序中设置一个计时器,每到一定时间,程序会得到相应的通知,但通知机制的实现者对我们的程序一无所知。而此时,就需有一个特定原型的函数指针,用这个指针来进行回调,来通知我们的程序事件已经发生。实际上,SetTimer()API使用了一个回调函数来通知计时器,而且,万一没有提供回调函数,它还会把一个消息发往程序的消息队列。
      
    另一个使用回调机制的API函数是EnumWindow(),它枚举屏幕上所有的顶层窗口,为每个窗口调用一个程序提供的函数,并传递窗口的处理程序。如果被调用者返回一个值,就继续进行迭代,否则,退出。EnumWindow()并不关心被调用者在何处,也不关心被调用者用它传递的处理程序做了什么,它只关心返回值,因为基于返回值,它将继续执行或退出。
      
    不管怎么说,回调函数是继续自C语言的,因而,在C++中,应只在与C代码建立接口,或与已有的回调接口打交道时,才使用回调函数。除了上述情况,在C++中应使用虚拟方法或函数符(functor),而不是回调函数。


    作者:桥头堡
    链接:https://www.zhihu.com/question/19801131/answer/27459821
    来源:知乎
    著作权归作者所有,转载请联系作者获得授权。


    展开全文
  • 2.回调函数 什么是回调函数呢?回调函数其实就是一个通过函数指针调用的函数!假如你把A函数的指针当作参数传给B函数,然后在B函数中通过A函数传进来的这个指针调用A函数,这就是回调机制。B函数就是回调函数。 3....
  • c语言回调函数的使用及实际作用详解

    千次阅读 多人点赞 2021-07-16 23:49:20
    回调函数这个知识点其实并不是很难,难是难在网上很多讲解回调函数的都说的太学术化了化了,一点也不亲民。 很多人即使知道怎么写回调函数也根本就搞不懂它们在实际产品中也有什么用,什么时候用。 所以这节课呢...

    大家好,我是无际。

    今天给大家讲一下芯片/模块厂家写SDK必须会使用的一种技术:回调函数。

    回调函数这个知识点其实并不是很难,难是难在网上很多讲解回调函数的都说的太学术化了化了,一点也不亲民。

    很多人即使知道怎么写回调函数也根本就搞不懂它们在实际产品中也有什么用,什么时候用。

    所以这节课呢我们会以程序架构的需求为出发点,讲解回调函数是怎么满足它这个需求的。

    为了方便大家理解,这篇内容也对应有一篇文章,大家可以找无际单片机编程获取。

    一、通过这节课程你能掌握以下知识:

    1. 掌握程序架构的核心理念或需求。
    2. 掌握回调函数的作用
    3. 掌握回调函数的程序编写
    4. 掌握回调函数在产品中的应用

    二、程序架构的核心理念和需求

    很多人可能会说一个好的程序架构啊,就是代码很紧凑、执行效率也很高。

    其实这个说的很片面,不完全对,这只能说明你程序算法写的好,但架构不一定做的好。

    即然是架构,那自然是以从”大局”为重,思维不能局限于当下的产品功能,还要考虑到以后功能的增加和裁剪,那么对于单片机开发来说,我认为一个好的程序架构至少要达到以下要求:

    硬件层和应用层的程序代码分开,相互之间的控制和通讯使用接口,而且不会共享的全局变量或数组。

    这里呢,我就这个要求,别小看这一个要求,因为这个要求里面蕴藏着很多学问的,比如用专业称为可移植性、可扩展性。

    那么我们来想象一下我们通常写单片机代码的方式啊,在51的时候基本一个.c文件解决,包括寄存器配置啊,产品功能啊。

    这种就是没有架构的程序,然后我们进化到STM32这个单片机以后,程序大了,慢慢也会在工程文件里加几个文件夹目录把硬件层和应用层代码分开了。

    于是我们会把一些不同的外设功能,比如Led、按键、串口等外设功能代码分别写在不同的.c文件里,然后统一用函数接口去调用它。

    比方说控制一个LED灯亮,直接在led.c文件里写一个驱动led灯状态的函数然后给外部调用就好了。

    那我们我们看这种Led的控制函数确实也是满足程序架构的需求的,硬件层和应用层代码分开,应用层用硬件层提供的接口来控制,而且又不会有硬件层和应用层共享的全部变量或数组。像这种是不是很简单?

    那么不知道你们有没有碰到另外一种情况,就是应用程序需要采集硬件层的数据,比如串口接收数据,按键采集、ADC值采集。

    这种硬件层的数据怎么通知应用层来拿,或者怎么主动给它?

    我们以往最简单粗暴的方式是不是就是用一个全局变量,比方说硬件层串口接收到数据来了,那么我们把数据丢到数组里,然后把接收完成全局变量标志位置1。

    比方说全局变量名为RcvFlag,然后应用层程序会轮询判断RcvFlag==1?是的话就开始把数组里的数据取出来解析。

    很多人就会说了,你看我用这种方法照样能实现功能啊,为什么还要学习别的架构。

    这样做当然可以实现功能,但是会存在移植性很差的问题。

    比如说你们老板让你把这个串口的硬件层封装起来给客户用,但不能让客户看到你实现的源代码,只提供接口(函数名)给对方用。

    那么这时候难道你要告诉客户先判断哪个变量为1,然后再取哪个数组的数据这么LOW的做法吗?

    那么如果是懂行的客户一定会怀疑你们公司的技术实力是不是小学生水平。

    那怎样做才会既方便又专业呢? 这里我们就需要用到回调函数啦。

    三、回调函数的作用

    那么在讲回调函数之前呢,对于函数调用呢我一般分为2种类型:

    1.输出型

    不知道大家有没有用过C语言自带的一些库函数,比如说sizeof()获取数据长度的函数,memcpy()是内存拷贝函数,我们调用这个函数之后呢就能完成相应的功能。

    还有我们基于单片机的一些程序函数,比方说控制LED点亮熄灭、继电器吸合断开、LCD驱动等等。

    那么这些呢,我一般称为输出型的函数。

    输出型函数我们是主导的角色,我们知道什么时候该调用它。

    2.输入型

    输入型呢,也称为的是响应式的函数

    什么叫响应式的函数呢?

    比方说接收串口的数据,我们不知道什么数据什么时候来。

    再比方说,我们按键检测的函数,我们不知道什么时候会按下按键,那么这些就要定义成响应式函数来实现,而响应式函数就可以用回调函数来实现

    所以通过这两个种类型的分析啊,我们就可以知道,回调函数基本是用在输入型的处理中。

    比方说串口数据接收,那么数据是输入到单片机里面的,单片机是处于从机角色。

    按键检测,按键状态是输入到单片机里的。

    再比方说ADC值采集,ADC值也是输入到单片机里的。

    那么它们输入的时间节点都是未知的,这些就能够用回调函数来处理。

    具体怎么处理后面我们会用代码来给大家举例。

    回调函数还有一个作用就是为了封装代码

    比如说做芯片或者模组的厂家,我们拿典型的STM32来举例,像外部中断、定时器、串口等中断函数都是属于回调函数,这种函数的目的是把采集到的数据传递给用户,或者说应用层。

    所以回调函数的核心作用是:

    1.把数据从一个.c文件传递到另一个.c文件,而不用全局变量共享数据这么LOW的方法。

    2.对于这种数据传递方式,回调函数更利于代码的封装。

    四、掌握回调函数的程序编写

    前面说了很多概念性的东西,可能大家也比较难理解,回调函数最终呢是靠函数指针来实现的。

    那么我这里通过一些模拟按键的例子来演示下怎么回通过调函数来处理它们。

    下面是我们的c-free工程,用这个来模拟方便点:

    从模块化编程的思想来看,整个工程分为2个部分,应用层main.c文件,硬件层key.c和key.h文件。

    不管再怎么复杂的程序,我们都要先从main函数一步步往下挖,main函数代码如下。

    int main(int argc, char *argv[])
    {
    KeyInit();
    KeyScanCBSRegister(KeyScanHandle);
    KeyPoll();
    
    return 0;
    }

    KeyInit();是key.c文件的按键初始化函数

    KeyScanCBSRegister(KeyScanHandle);是key.c的函数指针注册函数。

    这个函数可能大家会有点蒙,请跟进我们的节奏,下面开始烧脑环节,也是写回调函数的必须步骤,

    想理解这个回调函数注册函数,我们要先从硬件层(key.h)头文件的函数指针定义说起,具体看下图。

    这里自定义了一个函数指针类型,带两个形参。

    然后,我们在key.c这个文件里定义了一个函数指针变量。

    重点来了,我们就是通过这个函数指针,指向应用层的函数地址(函数名)

    具体怎么实现指向呢?就是通过函数指针注册函数。

    这个函数是在main函数里调用,使用这种注册函数的方式注册灵活性也很高,你想要在哪个.c文件使用按键功能就在哪里调用。

    这里要注意,main.c这个文件要定义一个函数来接收硬件层(key.c)过来的数据。

    这里定义也不是乱定义的,一定要和那个自定义函数指针类型返回值、形参一致。

    然后把这个函数名字直接复制给KeyScanCBSRegister函数的形参就可以了。

    这样调用后,我们key.c文件的pKeyScanCBS这个指针其实就是指向的KeyScanHandle函数。

    也就是说执行pKeyScanCBS的时候,就是执行KeyScanHandle函数。

    那具体检测按键的功能就是KeyPoll函数,这个在main函数里调用。

    当检测到键盘有输入以后,最终会调用pKeyScanCBS。

    最终执行的是main.c文件的KeyScanHandle函数。

    所以,我们来看下输出结果。

    如果还是有点模糊,下面我再给大家捋一捋编写和使用回调函数的流程:

    1. 自定义函数指针,形参作为硬件层要传到应用层的数据。
    2. 硬件层定义一个函数指针和函数指针注册函数。
    3. 应用层定义一个函数,返回值和形参都要和函数指针一致。
    4. 应用层调用函数指针注册函数,把定义好的函数名称作为形参传入。

    Ok,这就是回调函数的使用。

    如果还看不懂建议多看两遍。

    下面请大家思考一下,这个程序虽然简单,但是不是架构还不错?应用层和硬件层完全独立?

    展开全文
  • PHP回调函数用法及分析

    千次阅读 2019-09-03 11:50:47
    一、回调函数的概念 先看一下C语言里的回调函数回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。...
  • STM32串口通信例程,采用CubeMx进行配置,方便实用,是很不错的STM32通信例程。例程中使用了回调函数,可以让读者体会到回调函数的重要性
  • c++回调函数/ROS回调函数

    千次阅读 多人点赞 2018-01-09 10:15:58
    以下均是个人在实际耕码的过程中遇到的问题和整理的结果,可能会有不对的地方,望各位指正与交流 -------------------------------------------------我会有...c++中的回调函数: A "callback" is any function th...
  • 一直不太理解回调函数作用,下面是找到的一些关于回调函数作用的解答。 1.回调函数是一个很有用,也很重要的概念。当发生某种事件时,系统或其他函数将会自动调用你定义的一段函数。 2.回调函数就相当于一...
  • 回调函数(CALLBACK)的作用

    千次阅读 2018-10-22 22:22:18
    一般的调用(API)  系统程序员会给自己写的库留下一些接口,即API(application programming interface,应用编程接口),以供应用程序员使用。所以在抽象层的图示里,库位于应用的...通过回调函数的调用(CALLBAC...
  • 回调函数讲解

    2018-11-15 14:13:06
    通俗讲解回调函数作用,以及在何种情况下使用。中间函数的调用。
  • ucgui 窗口管理及回调函数的应用回调函数是由用户 定义的, 当特定事件发生时, 指示图像系统调用特定函数的函 数。 通常当窗口的内 容发生改变时, 他们用 来自 动重绘窗口
  • java回调函数作用以及运用

    千次阅读 2016-05-24 14:47:45
    模块之间总是存在这一定的接口,从调用方式上看,可以分为三类:同步调用、回调和异步调用。同步调用是一种阻塞式调用,也是我们在写程序中经常使用的;回调是一种双向的调用模式,也就是说,被调用的接口被调用时也...
  • 回调小思谈回调回调的形象说明程序里怎么做?最后附上本人涂鸦作. 谈回调 ...A调用了B的某个函数,然后B去执行这个函数,这个执行是有时间的,如果时间太长那么这时候就需要回调了.因为A不可能一直等B...
  • JS中回调函数(callback)理解

    万次阅读 多人点赞 2018-07-24 17:46:16
    今天有个刚入行的小兄弟问到了回调函数,讲解了一番以后觉得不能白讲,得把这些东西记下来,虽然很基础。。。 介绍 首先从英文介绍开始 A callback is a function that is passed as an argument to another ...
  • void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) ...{ //添加回调后的程序逻辑  if (htim->Instance == htim2.Instance) //判断是否定时器2 { } } void HAL_UART_RxCpltCallba...
  • 在类test1中调用函数print() ,把print()的函数指针传递给test1的函数指针参数 test1.h: #include #include using namespace std; typedef void (*FUNP)(); class test1 { public: void fun1(FUNP p) { (*p...
  • C/C++回调函数介绍

    2021-01-01 10:12:27
    对于很多初学者来说,往往觉得回调函数很神秘,很想知道回调函数的工作原理。本文将要解释什么是回调函数、它们有什么好处、为什么要使用它们等等问题,...如果想知道回调函数在实际中有什么作用,先假设有这样一种情况
  • lambda回调函数

    千次阅读 2017-11-20 17:04:58
    调用Python的lambda表达式会在运行时生成新的、未命名的函数对象。如果我们需要将其他传入的数据...作用一:可用lambda表达式代替 def 函数 import sys from tkinter import * root=Tk() Button(root, text='test',

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 260,259
精华内容 104,103
关键字:

回调函数的作用