Callback 函数
说明:此文章出自《深入浅出mfc》第6章的“Callback 函数”
Callback 函数
Hello的OnPaint在程序收到 WM_PAINT之后开始运作。为了让"Hello, MFC" 字样从天而降并有动画效果,程序采用LineDDA API 函数。我的目的一方面是为了示范消息的处理,一方面也为了示范 MFC 程序如何调用 Windows API 函数。许多人可能不熟悉LineDDA,所以我也一并介绍这个有趣的函数。
首先介绍 LineDDA:
void WINAPI LineDDA(int, int, int, int, LINEDDAPROC, LPARAM);
这个函数用来做动画十分方便,你可以利用前四个参数指定屏幕上任意两点的(x,y) 座标,此函数将以 Bresenham 算法(注) 计算出通过两点之直线中的每一个屏幕图素座标;每计算出一个坐标,就通知由LineDDA 第五个参数所指定的 callback 函数。这个callback 函数的型式必须是:
typedef void (CALLBACK* LINEDDAPROC)(int, int, LPARAM);
通常我们在这个 callback 函数中设计绘图动作。玩过Windows的接龙游戏吗?接龙成功后扑克牌的跳动效果就可以利用LineDDA 完成。虽然扑克牌的跳动路径是一条曲线,但将曲线拆成数条直线并不困难。LineDDA 的第六个(最后一个)参数可以视应用程序的需要传递一个32位指针,本例中Hello传的是一个Device Context。
Bresenham 算法是计算机图学中为了「显示器(屏幕或打印机)系由图素构成」的这个特性而设计出来的算法,使得求直线各点的过程中全部以整数来运算,因而大幅提升计算速度。
你可以指定两个坐标点,LineDDA 将以Bresenham 算法计算出通过两点之直线中每一个
屏幕图素的坐标。每计算出一个坐标,就以该坐标为参数,调用你所指定的callback 函数。
图6-6 LineDDA函数说明
你可以指定两个坐标点,LineDDA将以Bresenham 算法计算出通过两点之直线中每一个屏幕图素的坐标。每计算出一个坐标,就以该坐标为参数,调用你所指定的callback函数。
LineDDA 并不属于任何一个MFC 类,因此调用它必须使用C++ 的 "scope operator" (也就是 ::):
void CMyFrameWnd::OnPaint()
{
CPaintDC dc(this);
CRect rect;
GetClientRect(rect);
dc.SetTextAlign(TA_BOTTOM | TA_CENTER);
::LineDDA(rect.right/2, 0, rect.right/2, rect.bottom/2,
(LINEDDAPROC) LineDDACallback, (LPARAM) (LPVOID) &dc);
}
其中LineDDACallback是我们准备的callback 函数,必须在类中先有声明:
class CMyFrameWnd : public CFrameWnd
{
...
private:
static VOID CALLBACK LineDDACallback(int,int,LPARAM);
};
请注意,如果类的成员函数是一个callback 函数,你必须声明它为 "static",才能把C++ 编译器加诸于函数的一个隐藏参数this去掉(请看方块批注)。
以类的成员函数作为Windows callback函数
虽然现在来讲这个题目,对初学者而言恐怕是过于艰深,但我想毕竟还是个好机会--- 我可以在介绍如何使用callback 函数的场合,顺便介绍一些C++的重要观念。
首先我要很快地解释一下什么是callback 函数。凡是由你设计而却由 Windows系统调用的函数,统称为callback函数。这些函数都有一定的类型,以配合Windows的调用动作。
某些Windows API函数会要求以callback 函数作为其参数之一,这些API 例如SetTimer、LineDDA、EnumObjects。通常这种 API 会在进行某种行为之后或满足某种状态之时调用该 callback 函数。图 6-6 已解释过 LineDDA调用 callback 函数的时机;下面即将示范的 EnumObjects 则是在发现某个 Device Context 的 GDI object 符合我们的指定类型时,调用 callback 函数。
好,现在我们要讨论的是,什么函数有资格在 C++ 程序中做为 callback 函数?这个问题的背后是:C++ 程序中的 callback 函数有什么特别的吗?为什么要特别提出讨论?
是的,特别之处在于,C++ 编译器为类成员函数多准备了一个隐藏参数(程序代码中看不到),这使得函数类型与 Windows callback 函数的预设类型不符。
假设我们有一个CMyclass 如下:
class CMyclass {
private :
int nCount;
int CALLBACK _export
EnumObjectsProc(LPSTR lpLogObject, LPSTR lpData);
public :
void enumIt(CDC& dc);
}
void CMyclass::enumIt(CDC& dc)
{
// 注册callback 函数
dc.EnumObjects(OBJ_BRUSH, EnumObjectsProc, NULL);
}
C++ 编译器针对CMyclass::enumIt实际做出来的代码相当于:
void CMyclass::enumIt(CDC& dc)
{
CDC::EnumObjects(OBJ_BRUSH, EnumObjectsProc,
NULL, (CDC *)&dc);
}
你所看到的最后一个参数,(CDC *)&dc,其实就是this指针。类成员函数靠着this指针才得以抓到正确对象的数据。你要知道,内存中只会有一份类成员函数,但却可能有许多份类成员变量——每个对象拥有一份。
C++ 以隐晦的this指针指出正确的对象。当你这么做:
nCount = 0;
其实是:
this->nCount = 0;
基于相同的道理,上例中的 EnumObjectsProc 既然是一个成员函数,C++ 编译器也会为它多准备一个隐藏参数。
好,问题就出在这个隐藏参数。callback函数是给Windows调用用的,Windows 并不经由任何对象调用这个函数,也就无由传递this指针给callback 函数,于是导至堆栈中有一个随机变量会成为this指针,而其结果当然是程序的崩溃了。
要把某个函数用作callback函数,就必须告诉C++编译器,不要放this指针作为该函数的最后一个参数。两个方法可以做到这一点:
1. 不要使用类的成员函数(也就是说,要使用全局函数)做为callback 函数。
2. 使用static 成员函数。也就是在函数前面加上static修饰词。
第一种作法相当于在 C 语言中使用callback 函数。第二种作法比较接近 OO的精神。
我想更进一步提醒你的是,C++中的static成员函数特性是,即使对象还没有产生,static 成员也已经存在(函数或变量都如此)。换句话说对象还没有产生之前你已经可以调用类的static函数或使用类的static变量了。请参阅第二章。
也就是说,凡声明为static 的东西(不管函数或变量)都并不和对象结合在一起,它们是类的一部分,不属于对象。
获取更多帮主请关注小程序
Callback 函数相关推荐
- keras中的fit函数参数_keras的fit_generator与callback函数
fit_generator函数 fit_generator函数 callback类 每一个epoch结束(on_epoch_end)时,都要调用callback函数,callback函数(类)都要集成 ...
- javascript callback函数的理解与使用
最近做的一个项目中用到了callback函数,于是就研究了下总结下我对javascript callback的理解 首先从callback的字面翻译"回调" 可以理解这是一个函数被 ...
- 008_效果和动画的Callback函数
1. Callback函数在当前效果或动画100%完成之后执行. 2. jQuery动画的问题 2.1. 许多jQuery函数涉及动画.这些函数也许会将speed或duration作为可选参数. 2. ...
- Cython进阶--用Cython封装Callback函数
2019独角兽企业重金招聘Python工程师标准>>> Cython封装Callback函数 1 说明: 回调函数,在C语言里是经常要用到的,但是,在Python里封装一个C的回调函 ...
- MATLAB GUI如何创建Callback函数
本文以创建按钮的Callback函数为例介绍了在MATLAB如何在GUI中创建Callback函数 首先在MATLAB中输入guide,打开GUI文件,这里我随机打开一个我之前创建的GUI文件: 假设 ...
- java addcallback函数_java中怎么使用callback函数?
UYOU 在很多场景,作为开发都会想到,在执行完毕一个任务的时候,能执行一个callback函数是多么好的事情.现在模拟一下这个情景:定义三个类.分别是主函数类.callback函数的接口类.业务处理 ...
- CallBack函数 回调函数
CallBack函数 定义 回调函数就是一个通过函数指针调用的函数.如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用为调用它所指向的函数时,我们就说这是回调函数.回调函数不是由该函数 ...
- callback函数_Nodejs 源码解析 util.promisify 如何将 Callback 转为 Promise
Nodejs util 模块提供了很多工具函数.为了解决回调地狱问题,Nodejs v8.0.0 提供了 promisify 方法可以将 Callback 转为 Promise 对象. 工作中对于一些 ...
- 静态成员函数运用在CALLBACK函数和线程函数中《转载》
CALLBACK函数要声明称 static或全局,而在静态的函数中药调用非静态成员还不能调用,必须把这些成员也设置成静态的,不怎么方便,有好的解决方法吗. 其原因是把CALLBACK函数封装成C++类 ...
- Python进程池apply_async的callback函数不执行的解决方案
最近在用multiprocessing.Pool的apply_async方法做多进程,在写示例的时候发现callback居然没有执行,遂记录原因如下. 目录 apply_async的func传入lam ...
最新文章
- 2020年,人工智能如何走向高质量发展?
- SPI/I2S调试心得与经验总结
- secureCrT夜间模式
- ionic3 隐藏子页面tabs
- setTimeOut函数传参数
- oracle表参数,Oracle 表的创建 及相关参数
- Algs4-1.3.45栈的可生成性
- 原创:2016.4.25-2016.5.1 C# informal essay and tittle_tattle
- 手机安装python模块吗_1-Python-非root用户安装Python及Python模块
- ArcGIS建立拓扑并检查修改
- 2013 腾讯实习生招聘 武汉 一面
- PHP-SDK实现微信付款码支付
- 泰拉瑞亚正版大型服务器,泰拉瑞亚1.3.5.3物品大全-泰拉瑞亚1.3.5.3服务器版v1.3.5.3 安卓版-腾牛安卓网...
- 【软件工程】软工视频总结
- finalshell连接ubantu
- 计算机网络胡工程施工税率,弱电项目增值税6%、9%、13%税率怎样区分?项目经理必知...
- 段码液晶屏笔段电压范围_LCD段码(笔段)液晶显示屏和点阵液晶显示屏
- 关系抽取(二)远程监督方法总结
- 武汉理工大学博士生导师计算机,博士学位论文预答辩公告-武汉理工大学计算机学院.DOC...
- 教你用UltraISO制作启动光盘
热门文章
- 征服C指针---1.如何理解C指针的值和地址
- 世界杯:冰岛队竟然是强队
- JS 垃圾回收机制以及垃圾回收策略
- php shell地址,运用Shell 命令行获得本机IP地址
- 【yolov5+deepsort+tensorRT+QT+opencv临时启动内置跟踪器c++部署jetson-nx】
- 分布式存储Swift原理分析
- yansongda3支付宝APP支付返回空的问题
- VMware ESXi 8.0U1 Unlocker OEM BIOS 集成网卡驱动和 NVMe 驱动 (集成驱动版)
- 【Spring Boot 源码研究 】- 自动化装配条件化配置Conditional剖析
- 站长杂谈:这样的内容制作者真的很可爱