「c/c++笔记」我终于弄懂回调函数了

2023-05-28 0 593

明确提出难题

反弹表达式那个基本概念始终年来都对它处在忽然的认知中,总觉得无法较好的说明这玩意,前段时间在自学OpenCV的这时候又刚好遇见了它,因此,谁知此次我就把你给搞掂了吧。

先将OpenCV中的有关标识符附有

#include <iostream> #include <opencv2/core/core.hpp> #include <opencv2/highgui/highgui.hpp> #include <imgproc.hpp> using namespace std; using namespace cv; ​ //TrackBar出现出现改变的反弹表达式 void onChangeTrackBar(int pos, void* userdata); ​ //主表达式 int main() {    //trackbar的值    int posTrackBar = 0;    //trackbar的最小值    int maxValue = 255; ​    //初始化影像,以位图图方式初始化    Mat img = imread(“D:/code/repos/2.jpg”, 0); ​    //增建询问处    namedWindow(“二值化”);    imshow(“二值化”, img); ​    //创建trackbar,我们把img作为数据传进反弹表达式中    createTrackbar(“pos”, “二值化”, &posTrackBar, maxValue, onChangeTrackBar, &img);      waitKey();return 0; } ​ // 反弹表达式 void onChangeTrackBar(int pos, void* usrdata) {    // 强制类型转换    Mat src = *(Mat*)(usrdata);    Mat dst; ​    // 二值化threshold(src, dst, pos,255, 0);    imshow(“二值化”, dst); }

关于那个程序的有关效果,大家可以去看这篇文章。https://mp.toutiao.com/profile_v4/graphic/preview?pgc_id=6880767810209841677

查找资料

没有条件,创造条件也要上,不认知不要紧,赶紧去各大网站上搜索了一下关于反弹表达式的文章,受到了很多的启发,结合自己的认知,我对反弹表达式又有了一些新的想法,下面就来说说我对反弹表达式的看法。

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

上面这段说明出自于百度百科,看完之后相信你会有这种觉得,这说的是啥玩意?这。。。能好好说人话吗?其实很多这种基本概念你去翻阅比较官方的回答,都会有这种说明了跟没说明的觉得,完全无法认知嘛。

没事的,我也是这样的,大多数人估计都这样。

于是乎我去找了一下其它的回答:

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

作者:常溪玲 链接:

https://www.zhihu.com/question/19801131/answer/13005983

嗯?好像有点觉得了,我似乎听懂了那么一些,那么如果对应实际的程序标识符有事怎么一回事呢?

现在我们来整理一下思路:

需要一个反弹表达式需要一个调用反弹表达式的表达式

然后我们先创建一个反弹表达式,从前面的有关说明我们知道反弹表达式是一个通过表达式指针调用的表达式。简单来说它就是一个表达式而已,其它的可以先不用管。

void on_callbackBJ() {    printf(“I from beijing.\n”); } void on_callbackSH() {    printf(“I from shanghai.\n”); }

现在需要一个调用反弹表达式的函数,好的这也是个表达式,那么它有什么不同呢?我们可以将其叫做中介表达式,它就像一个中间商一样调用了另外一个表达式,并不是在表达式体内调用,而是通过形参调用,并且需要调用的是另一个表达式的地址,这样被调用那个表达式就成为反弹表达式。

void wherecallback(void *name()) {    printf(“where are you from?\n”);    name(); }

好的,我现在了解了这些,那么那个表达式到底有什么用处呢?为了弄明白这种表达式的奥妙,首先明确提出三个难题:

反弹表达式是什么东西?反弹表达式怎么开发,怎么使用?反弹表达式的作用,应该在什么情况下使用?

打开百度搜索了好多资料,如下:

第一个难题:

其实反弹就是一种利用表达式指针进行表达式调用的过程.

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

使用反弹!

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

看了这么多的资料,我只将每位的定义总结一下就一句话:反弹表达式就是表达式指针的一种用法。

第二个难题:

我实现了一个很简单的反弹表达式。

#include <stdio.h>void on_callbackBJ() {    printf(“I from beijing.\n”); } void on_callbackSH() {    printf(“I from shanghai.\n”); } void wherecallback(void* name()) {    printf(“where are you from?\n”);    name(); } int main() {    wherecallback(on_callbackBJ);    wherecallback(on_callbackSH);return 0; }

第三个难题:

用过STL的人都知道,在STL中众多算法和程序都用到反弹表达式,这实现了一种策略。只要任何符合我的标准的表达式和计算都可以用我那个公式。你可以实现各种各样的反弹表达式,只要符合我的格式就能用。

就上面的程序来说,你只要表达式格式符合cllback第二个参数的格式不论你给别人做饭、铺床叠被都可以正常工作。这就是反弹的作用,把反弹实现留给别人。

小结

综合上面的回答,我认知的反弹表达式及其作用就是:我自己写了一个实现特定功能的表达式B,那个就是反弹表达式。当我在调用某个API表达式时,那个表达式的某个参数需要一个指向表达式的指针,也就是需要一个反弹表达式,我并不需要关心那个API表达式的内部是什么,只需要知道它所实现的功能以及各个参数的含义,然后将我所编写的实现特定功能的表达式B作为参数传入进去,当然我也可以传入表达式C或者表达式D之类满足我需要的表达式。

回归正题

OK,现在返回到正题中去,如何认知OpenCV中的反弹表达式,如前文的分析,我现在需要调用一个OpenCV的一个API表达式creatTrackbar(),他的表达式原型:

int createTrackbar(conststring& trackbarname, conststring& winname, int* value, int count, TrackbarCallback onChange=0,void* userdata=0);

我不需要知道这表达式内部是什么样的,我需要它各个参数的含义:

第一个参数,const string&类型的trackbarname,表示轨迹条的名字,用来代表我们创建的轨迹条。

第二个参数,const string&类型的winname,填询问处的名字,表示那个轨迹条会依附到哪个询问处上,即对应namedWindow()创建询问处时填的某一个询问处名。

第三个参数,int* 类型的value,一个指向整型的指针,表示滑块的位置。并且在创建时,滑块的初始位置就是该变量当前的值。

第四个参数,int类型的count,表示滑块可以达到的最大位置的值。PS:滑块最小的位置的值始终为0。

第五个参数,TrackbarCallback类型的onChange,首先注意他有默认值0。这是一个指向反弹表达式的指针,每次滑块位置出现改变时,那个表达式都会进行反弹。并且那个表达式的原型必须为void XXXX(int,void*);其中第一个参数是轨迹条的位置,第二个参数是用户数据(看下面的第六个参数)。如果反弹是NULL指针,表示没有反弹表达式的调用,仅第三个参数value有变化。

第六个参数,void*类型的userdata,也有默认值0。那个参数是用户传给反弹表达式的数据,用来处理轨迹条事件。如果使用的第三个参数value实参是全局变量的话,完全可以不去管那个userdata参数。

以及它的作用:

那个createTrackbar表达式,为我们创建一个具有特定名称和范围的轨迹条(Trackbar,或者说是滑块范围控制工具),指定一个和轨迹条位置同步的变量。而且要指定反弹表达式onChange(第五个参数),在轨迹条位置出现改变的这时候来调用那个反弹表达式。并且我们知道,创建的轨迹条显示在指定的winname(第二个参数)所代表的询问处上。

剩下的就是反弹表达式的处理,想要什么的功能就写什么样的表达式传进去,如果我要实现二值化的处理,我会这样

void onChangeTrackBar(int pos, void* usrdata) {    // 强制类型转换    Mat src = *(Mat*)(usrdata);    Mat dst; ​    // 二值化    threshold(src, dst, pos, 255, 0);    imshow(“二值化”, dst); }

结果:

「c/c++笔记」我终于弄懂回调函数了
「c/c++笔记」我终于弄懂回调函数了

如果我要实现两张图片的混合,并且可以调节其透明度我会这样:

void on_Trackbar(int , void* ) { alphaVal = (double)alphaValSlider / maxAlphaVal; betaVal = (1.0– alphaVal); ​ addWeighted(src1, alphaVal, src2, betaVal,0.0, dst); imshow(“滑动询问处”,dst); }

结果:

「c/c++笔记」我终于弄懂回调函数了
「c/c++笔记」我终于弄懂回调函数了

相关文章

发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务