「c/c++笔记」我终于弄懂回调函数了
liebian365 2024-11-12 13:06 22 浏览 0 评论
提出问题
回调函数这个概念一直以来都对它处于迷迷糊糊的理解中,总感觉不能很好的解释这玩意儿,最近在学习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);
}
结果:
如果我要实现两张图片的混合,并且可以调节其透明度我会这样:
void on_Trackbar(int , void* )
{
alphaVal = (double)alphaValSlider / maxAlphaVal;
betaVal = (1.0 - alphaVal);
?
addWeighted(src1, alphaVal, src2, betaVal, 0.0, dst);
imshow("滑动窗口",dst);
}
结果:
相关推荐
- 4万多吨豪华游轮遇险 竟是因为这个原因……
-
(观察者网讯)4.7万吨豪华游轮搁浅,竟是因为油量太低?据观察者网此前报道,挪威游轮“维京天空”号上周六(23日)在挪威近海发生引擎故障搁浅。船上载有1300多人,其中28人受伤住院。经过数天的调...
- “菜鸟黑客”必用兵器之“渗透测试篇二”
-
"菜鸟黑客"必用兵器之"渗透测试篇二"上篇文章主要针对伙伴们对"渗透测试"应该如何学习?"渗透测试"的基本流程?本篇文章继续上次的分享,接着介绍一下黑客们常用的渗透测试工具有哪些?以及用实验环境让大家...
- 科幻春晚丨《震动羽翼说“Hello”》两万年星间飞行,探测器对地球的最终告白
-
作者|藤井太洋译者|祝力新【编者按】2021年科幻春晚的最后一篇小说,来自大家喜爱的日本科幻作家藤井太洋。小说将视角放在一颗太空探测器上,延续了他一贯的浪漫风格。...
- 麦子陪你做作业(二):KEGG通路数据库的正确打开姿势
-
作者:麦子KEGG是通路数据库中最庞大的,涵盖基因组网络信息,主要注释基因的功能和调控关系。当我们选到了合适的候选分子,单变量研究也已做完,接着研究机制的时便可使用到它。你需要了解你的分子目前已有哪些...
- 知存科技王绍迪:突破存储墙瓶颈,详解存算一体架构优势
-
智东西(公众号:zhidxcom)编辑|韦世玮智东西6月5日消息,近日,在落幕不久的GTIC2021嵌入式AI创新峰会上,知存科技CEO王绍迪博士以《存算一体AI芯片:AIoT设备的算力新选择》...
- 每日新闻播报(September 14)_每日新闻播报英文
-
AnOscarstatuestandscoveredwithplasticduringpreparationsleadinguptothe87thAcademyAward...
- 香港新巴城巴开放实时到站数据 供科技界研发使用
-
中新网3月22日电据香港《明报》报道,香港特区政府致力推动智慧城市,鼓励公私营机构开放数据,以便科技界研发使用。香港运输署21日与新巴及城巴(两巴)公司签署谅解备忘录,两巴将于2019年第3季度,开...
- 5款不容错过的APP: Red Bull Alert,Flipagram,WifiMapper
-
本周有不少非常出色的app推出,鸵鸟电台做了一个小合集。亮相本周榜单的有WifiMapper's安卓版的app,其中包含了RedBull的一款新型闹钟,还有一款可爱的怪物主题益智游戏。一起来看看我...
- Qt动画效果展示_qt显示图片
-
今天在这篇博文中,主要实践Qt动画,做一个实例来讲解Qt动画使用,其界面如下图所示(由于没有录制为gif动画图片,所以请各位下载查看效果):该程序使用应用程序单窗口,主窗口继承于QMainWindow...
- 如何从0到1设计实现一门自己的脚本语言
-
作者:dong...
- 三年级语文上册 仿写句子 需要的直接下载打印吧
-
描写秋天的好句好段1.秋天来了,山野变成了美丽的图画。苹果露出红红的脸庞,梨树挂起金黄的灯笼,高粱举起了燃烧的火把。大雁在天空一会儿写“人”字,一会儿写“一”字。2.花园里,菊花争奇斗艳,红的似火,粉...
- C++|那些一看就很简洁、优雅、经典的小代码段
-
目录0等概率随机洗牌:1大小写转换2字符串复制...
- 二年级上册语文必考句子仿写,家长打印,孩子照着练
-
二年级上册语文必考句子仿写,家长打印,孩子照着练。具体如下:...
你 发表评论:
欢迎- 一周热门
- 最近发表
- 标签列表
-
- wireshark怎么抓包 (75)
- qt sleep (64)
- cs1.6指令代码大全 (55)
- factory-method (60)
- sqlite3_bind_blob (52)
- hibernate update (63)
- c++ base64 (70)
- nc 命令 (52)
- wm_close (51)
- epollin (51)
- sqlca.sqlcode (57)
- lua ipairs (60)
- tv_usec (64)
- 命令行进入文件夹 (53)
- postgresql array (57)
- statfs函数 (57)
- .project文件 (54)
- lua require (56)
- for_each (67)
- c#工厂模式 (57)
- wxsqlite3 (66)
- dmesg -c (58)
- fopen参数 (53)
- tar -zxvf -c (55)
- 速递查询 (52)