精华内容
下载资源
问答
  • 那么,回调函数是个什么鬼呢?它和函数到底有何异同?既然已经有了函数,为啥还非要生出个回调函数来?想必,小伙伴们在刚碰到这个概念的时候,都会被这些问题困扰。网上搜一搜,有很多相关的材料,但是未必透彻。我...

    在C/C++里面,函数的概念很好理解,就是把某个任务独立出来,封装在一起,然后给它取个名字,它可以有参数和返回值。那么,回调函数是个什么鬼呢?它和函数到底有何异同?既然已经有了函数,为啥还非要生出个回调函数来?想必,小伙伴们在刚碰到这个概念的时候,都会被这些问题困扰。网上搜一搜,有很多相关的材料,但是未必透彻。我觉得要真正理解一个概念,必须要先理解它存在的意义,也就是它为什么要存在,它能带来什么方便之处。在这一点上C++ Primer这本书写的还是比较到位的。仔细阅读之后,我把自己的心得写下来,供大家参考。


    首先,回调函数也是函数,就像白马也是马一样。它具有函数的所有特征,它可以有参数和返回值。其实,单独给出一个函数是看不出来它是不是回调函数的。回调函数区别于普通函数在于它的调用方式。只有当某个函数(更确切的说是函数的指针)被作为参数,被另一个函数调用时,它才是回调函数。就像给你一碗饭,你并不能说它是中饭还是晚饭一样,只有当你在某个时候把它吃掉了你才明确它是中饭还是晚饭(这个比喻貌似有点挫。领会精神就好,哈哈)。

    那么问题来了,为什么我们要把函数作为参数来调用呢,直接在函数体里面调用不好吗?这个问题问的好。在这个意义上,“把函数做成参数”和“把变量做成参数”目的是一致的,就是以不变应万变。形参是不变的,而实参是变的。唯一不同的是,普通的实参可以由计算机程序自动产生,而函数这种参数计算机程序是无法自己写出来的,因为函数本身就是程序(要是程序可以写程序的话那就是超级人工智能了),它必须由人来写。所以对于回调函数这种参数而言,它的“变”在于人有变或者人的需求有变。

    C++ Primer里面举了个例子就是排序算法。为了使排序算法适应不同类型的数据,并且能够按各种要求进行排序,机智的人类把排序算法做成了一个模版(在标准模版库STL里),并且把判断两个数据之间的“大小”(也可以是“字节数”,或者其他某种可以比较的属性)这个任务(即函数)当成一个参数放在排序算法这个函数的参数列表里,而把它的具体实现就交给了使用排序算法的人。这个判断大小的函数就是一个回调函数。比如我们要给某个vector容器里面的单词进行排序,我们就可以声明一个排序算法:

    void stable_sort(vector<string>::iterator iterBegin, vector<string>::iterator iterEnd, 
    bool (*isShorter)(const string &, const string &));

    其中前面两个是普通参数,即迭代器(用于标记vector容器里面元素的位置),而第三个参数isShorter就是回调函数。根据不同需求isShorter可以有不同的实现,包括函数名。比如:

    bool myIsShorter(const string &s1, const string &s2)
    {    
         return s1.size()<s2.size();
    }
    stable_sort(words.begin(),words.end(),myIsShorter);
    
    根据需求你也可以换一种方式来实现。注意,在传递myIsShorter这个参数时,只需写函数名,它代表函数指针。后面绝对不能加()和参数,绝对不能加()和参数,绝对不能加()和参数!因为那样是调用函数的返回值!两者天壤之别!在stable_sort运行时,当遇到需要比较两个单词的长短时,就会对myIsShorter进行调用,得到一个判断。在调用时,还必须把两个单词传递给isShorter供isShorter调用。所以说stable_sort调用了myIsShorter,而myIsShorter又调用了stable_sort给它的单词。它们相互调用。这就是“回调”这两个字的含义!

    虽然说形参不变,实参可变,以不变应万变。但是作为实参有一点还是不能变的,那就是实参的数据类型不能变。比如void foo(int i)这个函数里的参数i可以取1也可以取2,但是它必须是整型的。同样的,回调函数这种参数的类型也不能变。而函数的类型是由函数的参数类型和返回值类型决定的。比如前面提到的排序算法里面,isShorter这个回调函数的参数必须是两个const string类型,返回值必须是bool类型。所以在写回调函数时还是不能太任性,必须要查看一下调用该回调函数的函数的声明。[这里插播一段广告。假如回调函数本应该只有两个参数的,但是我想让它更普适一点,我想传三个参数给它,那怎么办呢?为了解决这个问题,C++标委又发明了lambda表达式和bind函数这两种方法。欲知详情请戳这里这里。]

    总之,所谓回调函数就是把函数当作参数使用。目的是使程序更加普适(正如活字印刷,把可能会“变”的字一个个分离开来,这样就可以任意组合,重复利用)。一般情况下,一个人的小规模程序用不着这种普适性,除非你想把它做成工具箱(比如游戏引擎),供他人使用。

    其实实现这种普适性还有其他方法,比如对虚函数进行重写(或者用纯虚函数。Objective C里面所有函数都是虚函数,而协议相当于纯虚函数)。这样同一个函数就可以有不同的实现。不同的合作者之间就可以通过这种虚函数“协议”进行合作。


    水平有限,如有不妥,欢迎拍砖!


    参考文献

    C++ Primer


    展开全文
  • 看下面的图片: [img=https://img-bbs.csdn.net/upload/201403/23/1395567991_616971.png][/img] 这段话什么意思?难道像【printf】这样的函数名,其本质是一个数值吗?
  • 指针数组,数组指针,函数指针,main函数实质,二重指针,函数指针作为参数,泛型函数.pdf
  • 函数指针和指针函数用法和区别

    万次阅读 多人点赞 2018-05-24 08:11:10
    函数指针和指针函数,在学习 C 语言的时候遇到这两个东西简直头疼,当然还有更头疼的,比如什么函数指针函数、指针函数指针、数组指针、指针数组、函数指针数组等等,描述越长其定义就越复杂,当然理解起来就越难,...

    前言

    函数指针和指针函数,在学习 C 语言的时候遇到这两个东西简直头疼,当然还有更头疼的,比如什么函数指针函数、指针函数指针、数组指针、指针数组、函数指针数组等等,描述越长其定义就越复杂,当然理解起来就越难,特别是刚开始学习这门语言的童鞋,估计碰到这些东西就已经要崩溃了,然后好不容易死记硬背下来应付考试或者面试,然后过了几天发现,又是根本不会用,也不知道该在哪些地方用,这就尴尬了。
    今天这里只讲两个相对简单的,其实上面说那些太复杂的东西也真的很少用,即便是用了理解起来很麻烦,所以莫不如先深刻理解这两个比较容易的,并且项目中比较常用到。

    正文

    先来看看两者的定义以及说明。

    指针函数

    定义

    指针函数,简单的来说,就是一个返回指针的函数,其本质是一个函数,而该函数的返回值是一个指针。
    声明格式为:*类型标识符 函数名(参数表)

    这似乎并不难理解,再进一步描述一下。
    看看下面这个函数声明:

    int fun(int x,int y);
    

    这种函数应该都很熟悉,其实就是一个函数,然后返回值是一个 int 类型,是一个数值。
    接着看下面这个函数声明:

    int *fun(int x,int y);
    

    这和上面那个函数唯一的区别就是在函数名前面多了一个*号,而这个函数就是一个指针函数。其返回值是一个 int 类型的指针,是一个地址。

    这样描述应该很容易理解了,所谓的指针函数也没什么特别的,和普通函数对比不过就是其返回了一个指针(即地址值)而已。

    指针函数的写法

    int *fun(int x,int y);
    int * fun(int x,int y);
    int* fun(int x,int y);
    

    这个写法看个人习惯,其实如果*靠近返回值类型的话可能更容易理解其定义。

    示例

    (由于本人习惯于 Qt 中进行开发,所以这里为了方便,示例是在 Qt 工程中写的,其语法是一样的,只是输出方式不同)
    来看一个非常简单的示例:

    typedef struct _Data{
        int a;
        int b;
    }Data;
    
    //指针函数
    Data* f(int a,int b){
        Data * data = new Data;
        data->a = a;
        data->b = b;
        return data;
    }
    
    int main(int argc, char *argv[])
    {
        QApplication a(argc, argv);
        //调用指针函数
        Data * myData = f(4,5);
        qDebug() << "f(4,5) = " << myData->a << myData->b;
    
        return a.exec();
    }
    
    

    输出如下:

    f(4,5) =  4 5
    

    注意:在调用指针函数时,需要一个同类型的指针来接收其函数的返回值。
    不过也可以将其返回值定义为 void*类型,在调用的时候强制转换返回值为自己想要的类型,如下:

    //指针函数
    void* f(int a,int b){
        Data * data = new Data;
        data->a = a;
        data->b = b;
        return data;
    }
    
    调用:
    Data * myData = static_cast<Data*>(f(4,5));
    

    其输出结果是一样的,不过不建议这么使用,因为强制转换可能会带来风险。

    函数指针

    定义

    函数指针,其本质是一个指针变量,该指针指向这个函数。总结来说,函数指针就是指向函数的指针。
    声明格式:类型说明符 (*函数名) (参数)
    如下:

    int (*fun)(int x,int y);
    

    函数指针是需要把一个函数的地址赋值给它,有两种写法:

    fun = &Function;
    fun = Function;
    

    取地址运算符&不是必需的,因为一个函数标识符就表示了它的地址,如果是函数调用,还必须包含一个圆括号括起来的参数表。

    调用函数指针的方式也有两种:

    x = (*fun)();
    x = fun();
    

    两种方式均可,其中第二种看上去和普通的函数调用没啥区别,如果可以的话,建议使用第一种,因为可以清楚的指明这是通过指针的方式来调用函数。当然,也要看个人习惯,如果理解其定义,随便怎么用都行啦。

    示例

    int add(int x,int y){
        return x+y;
    }
    int sub(int x,int y){
        return x-y;
    }
    //函数指针
    int (*fun)(int x,int y);
    
    int main(int argc, char *argv[])
    {
        QApplication a(argc, argv);
        //第一种写法
        fun = add;
        qDebug() << "(*fun)(1,2) = " << (*fun)(1,2) ;
    	//第二种写法
        fun = &sub;
        qDebug() << "(*fun)(5,3) = " << (*fun)(5,3)  << fun(5,3);
    
        return a.exec();
    }
    
    

    输出如下:

    (*fun)(1,2) =  3
    (*fun)(5,2) =  2 2
    

    上面说到的几种赋值和调用方式我都分别使用了,其输出结果是一样的。

    二者区别

    通过以上的介绍,应该都能清楚的理解其二者的定义。那么简单的总结下二者的区别:

    定义不同

    指针函数本质是一个函数,其返回值为指针。
    函数指针本质是一个指针,其指向一个函数。

    写法不同

    指针函数:int* fun(int x,int y);
    函数指针:int (*fun)(int x,int y);
    可以简单粗暴的理解为,指针函数的*是属于数据类型的,而函数指针的星号是属于函数名的。
    再简单一点,可以这样辨别两者:函数名带括号的就是函数指针,否则就是指针函数。

    用法不同

    上面已经写了详细示例,这里就不在啰嗦了。

    总而言之,这两个东西很容易搞混淆,一定要深入理解其两者定义和区别,避免犯错。

    另外,本文都是针对普通函数指针进行介绍,如果是C++非静态成员函数指针,其用法会有一些区别,在另外一篇博客中单独介绍,文章在这里

    展开全文
  • 钩子函数什么

    万次阅读 2018-08-25 18:40:33
    什么是钩子函数? 先来看一段百科:钩子函数是Windows消息处理机制的一部分,通过设置“钩子”,应用程序可以在系统级对所有消息、事件进行过滤,访问在正常情况下无法访问的消息。钩子的本质是一段用以处理系统...

    什么是钩子函数?

    先来看一段百科:钩子函数是Windows消息处理机制的一部分,通过设置“钩子”,应用程序可以在系统级对所有消息、事件进行过滤,访问在正常情况下无法访问的消息。钩子的本质是一段用以处理系统消息的程序,通过系统调用,把它挂入系统。

    emmmm,认认真真读三遍。get关键词,在系统级对所有消息进行过滤,这是什么意思呢?是说钩子函数是在一个事件触发的时候,在系统级捕获到了他,然后做一些操作。一段用以处理系统消息的程序,用以处理系统消息的程序,是说钩子函数是用于处理系统消息的。

    总结一下:

    钩子函数: 1、是个函数,在系统消息触发时被系统调用  2、不是用户自己触发的

     

    钩子函数的名称是确定的,当系统消息触发,自动会调用。例如react的componentWillUpdate函数,用户只需要编写componentWillUpdate的函数体,当组件状态改变要更新时,系统就会调用componentWillUpdate。

     

    常见的钩子函数:

    react的生命周期函数、vue的生命周期函数等

    展开全文
  • 函数本质

    2020-09-29 16:03:42
    整个程序分为多个源文件:一个文件分成多个函数,一个函数分成多个语句,这是整个程序的组织形式 这样组织的好处:分化问题,便于编写程序。便于分工 函数的出现是程序员和架构师的...函数实质:数据处理器 程序的主

    整个程序分为多个源文件:一个文件分成多个函数,一个函数分成多个语句,这是整个程序的组织形式
    这样组织的好处:分化问题,便于编写程序。便于分工
    函数的出现是程序员和架构师的需要,而不是编译器、cpu的需要
    函数的目的是实现模块化编程,说白了就是为了提供程序的可移植性

    函数是动词,变量是名词
    函数将来被编译成可执行代码段,变量(主要指全局变量),经过编译后变成数据或者在运行时变成数据,一个程序的运行需要代码和数据两方向的结合才能完成
    代码是为了加工数据,数据必须借助代码来起作用

    函数的实质:数据处理器
    程序的主题是数据,程序运行的主要目标是生成目标数据。程序的编写和运行就是为了吧元数据加工成目标数据
    函数就是程序的一个缩影,函数的参数列表其实就是为了给函数输入原材料数据,函数的返回值和输出型参数就是为了向外部输出目标数据,函数的函数体里的那些代码就是加工算法。
    整个程序的运行其实就是很多歌函数相继运行的连续过程。

    函数的基本使用
    定义:函数体 声明:函数原型 调用:使用函数
    函数定义时函数的根本,函数定义中的函数名表示了这个函数在内存中的首地址,所以可以用函数名来调用执行这个函数(实质上是指针解引用访问);函数定义中的函数体是函数的执行关键
    函数声明的主要作用是告诉编译器函数的原型 函数调用时调用执行一个函数

    函数原型和作用
    函数原型就是函数的生命,说白了就是函数的函数名、返回值类型、参数列表
    函数原型的主要作用就是给编译器提供原型,让编译器在编译程序时帮我们进行参数的静态类型检查
    编译器在编译程序时是以单个源文件为单位的而且编译器工作室已经经过预处理处理了,编译文件时是按照语句顺序前后编译的

    展开全文
  • 操作符重载函数,仍然是一个成员函数。和普通的成员函数调用一样。我们知道,类的成员函数必须通过对象才可以调用,比如“[ ]” 运算符。 class test{public: int operator[](int pos);}; 要调用 [] 运算符重载成员...
  • JavaScript中什么是构造函数?

    千次阅读 多人点赞 2016-04-02 23:18:32
    经过一番思索,决定从JavaScript 的函数本质开始说起。(以下简称js) 要想搞明白构造函数,就要先弄懂什么是函数?以及js中有哪几种函数的创建形式。 什么是函数? 函数简单的说就是重复执行的代码块;按照JavaScript...
  • 简单的理解:函数是运行在本地的,方法是公用的。 事件是开关,通过某某事件触发某个函数 通常命名规范 函数的命名使用小写字母和下划线(小写字母开头)的方式,例如get_client_ip; 方法的命名使用驼峰法(首...
  • 极大似然估计(Maximum Likelihood Estimattion Theory)是什么?极大似然估计的本质思想是什么?为什么极大似然可以作为损失函数使用?负对数似然损失函数(Negative.pdf
  • 内联函数实质

    2017-12-14 22:38:42
    内联函数实质及与静态函数、宏定义的区别
  • 什么是钩子函数

    千次阅读 2018-11-15 10:53:31
    参考百度百科讲解,钩子函数是Windows消息处理机制的一部分,通过设置“钩子”,应用程序...钩子的本质是一段用以处理系统消息的程序,通过系统调用,把它挂入系统。 钩子是在捕获消息的时候立即执行钩子函数。 ...
  • 函数/函数名的本质

    2017-11-14 10:49:37
    函数本质是一段可执行性的代码,函数名则指向这段代码的首地址,函数名实际上就是一个存储可函数入口地址的常量,即func == &func;
  • Lerp 函数本质理解

    万次阅读 2018-12-06 17:48:26
    今天写了两个知识点,发现 ...Unity 中好几个 Lerp 函数,而对于 Lerp 函数本质,我以前只知道个大概,参考了别人写的,觉得还是不够清晰,截图加上自己的详细理解如下: 参考资料:http://www.manew.com/blog...
  • 什么是目标函数?定义是:指所关心的目标与相关的因素的函数关系。举个例子,假如我们想要预测公司楼下手抓饼店明天能卖多少张手抓饼,已知过去10天每天卖多少,以及每天的天气情况,是否有节假日,和手抓饼店老板和...
  • 神经网络激活函数的作用是什么

    万次阅读 多人点赞 2017-12-03 21:22:29
    在看到激活函数的时候,突然不知道激活函数是干嘛用的!立马翻遍手里的纸质资料,都说的模糊不清。无奈,赶紧来网上看看!下面,我就把在网上看到的知识点,总结一下,形成读书笔记,方便大家学习!由于本人知识有限...
  • 什么是函数?

    千次阅读 2019-01-02 00:07:29
    什么是函数? 似乎每次做FFT都需要加窗函数,很少有不加窗函数的时候。那为什么要加窗函数,加窗有什么好处,又有什么坏处呢,凭什么目的来加函数呢? 1. 为什么要加窗 在《什么是泄漏?》中已经讲到每次FFT变换...
  • 箭头函数、没有prototype、没有自己的this指向、不可以使用arguments、自然不可以new。 let fun = (a, s) => {}; console.dir(fun); // --- > 从
  • 函数重载概念本质

    千次阅读 2015-06-08 19:47:45
    1 函数重载概念 函数重载(Function Overload) 用同一个函数名定义不同的函数函数名和不同的参数搭配时函数的含义不同 2 函数重载的判断标准 函数重载至少满足下面的一个条件: 参数个数不同 参数类型...
  • softmax函数,它并不十分复杂,但又不是十分简单,总之这个函数就是很难去背诵下来,以致于每次你要写softmax的时候,都得搜一下才行。而每次搜索softmax,都对你的信心是一次小规模打击——这么简单的数学公式我还...
  • 多分类loss函数本质理解

    万次阅读 2018-09-03 17:32:10
     1、损失函数本质在数学上称为目标函数;这个目标函数的目标值符合最完美的需求;损失函数的目标值肯定是0,完美分类的损失必然为0 ;  2、损失函数分为两部分,一部分为正确的分类,一部分为错误的分类;保留...
  • nline函数和virtual函数有着本质的区别,inline函数是在程序被编译时就展开,在函数调用处用整个函数体去替换,而virtual函数是在运行期才能够确定如何去调用的,因而inline函数体现的是一种编译期机制,virtual函数...
  • 什么是连续函数

    千次阅读 2019-04-01 16:59:28
    在微积分中,什么是连续函数?这是一个根本问题,马虎不得。 科普中国,完全避开拓扑结构,只谈自变量与因变量的微小变化,……让人哭笑不得。这就是中国的可科普水平? 问题的实质是:给定函数f : X→Y,X与Y的拓...
  • 回调函数函数到底在干什么啊?

    千次阅读 2018-03-15 21:31:05
    其实回调函数本质是一个函数指针,也就是一个指向函数的指针(请联想变量指针,如int *a)。回调函数一般不是直接调用,而是由另一个函数调用,也就是把回调函数作为一个参数传递给另一个函数。既然有回调函数,那回...
  • 指针函数函数指针

    万次阅读 多人点赞 2019-03-30 16:21:36
    概述 指针函数函数指针是C语言里两个比较绕的概念。但是不仅面试题爱考,实际应用中也比较广泛。很多人因为搞不清这两个概念,干脆就避而远之,我刚接触C语言的时候对...指针函数: 顾名思义,它的本质是一个函数...
  • 函数本质

    千次阅读 2015-03-27 17:40:17
    一直不明白虚函数本质,在看了>之后,豁然开朗,下面就自己总结一下。 虚函数之所以能够做到动态调用,是因为程序在执行阶段才确定调用,也就是晚绑定。而早绑定在编译阶段就已经确定下一步该调用哪个函数。 那么...
  • 什么是函数式编程?

    万次阅读 2018-08-22 11:21:35
    当我们说起函数式编程来说,我们会看到如下函数式编程的长相: 函数式编程的三大特性: immutable data 不可变数据:像Clojure一样,默认上变量是不可变的,如果你要改变变量,你需要把变量copy出去修改。这样一来...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 681,339
精华内容 272,535
关键字:

函数的本质是什么