C++中以类的成员函数作为Windows callback函数需要设置成static函数
在看代码时,发现很多CALLBACK函数,所以仔细研究了一下C++中的CALLBACK函数
首先,我们来理解一下,什么是C++中的CALLBACK函数 => 凡是由你设计,但是由Windows操作系统调用的函数,我们把它统称为CALLBACK函数,这些函数都有一定的类型,以方便配合Windows的调用动作
某些Windows API函数会要求以callback函数作为其参数之一,这些API 比如SetTimer,LineDDA等。这些Windows API会在进行某种行为之后或者满足某种状态之时调用该callback函数。
看代码时发现,基本上CALLBACK函数都是全局函数,而不是类的成员函数。也就是说通常用全局函数(不要用类的成员函数)来作为CALLBACK函数。 =》 那么问题来了,为什么要这样? 可以采用类的成员函数来作为CALLBACK函数么? =》答案是可以的,但是你要把类的成员函数设置成static类型 => 为什么会这样呢
这是因为C++编译器为类成员函数多准备了一个隐藏参数 (程序代码中看不到), 这使得函数类型与Windows callback函数的预设类型不符.
我们来看一段代码
class CMyclass {
private :
int nCount;
int CALLBACK _export
EnumObjectProc(LPSTR lpObj, LPSTR lpData);
public:
void enumIt(CDC& dc);
}
void CMyclass::enumIt(CDC& dc)
{
//注册CALLBACK函数
CDC::EnumObjects(OBJ_BRUSH,EnumObjectProc,NULL);
}
C++编译器,对上面CMyclass类中的成员函数enumIt实际做出来的代码,就相当于
void CMyclass::enumIt(CDC& dc) { //注册CALLBACK函数 CDC::EnumObjects(OBJ_BRUSH,EnumObjectProc,NULL, (CDC*)&dc); }
在这里,你所看到新增加的最后一个参数 (CDC*)&dc, 其实就是一个this指针,类成员函数靠着this指针才得以抓到正确对象的数据.
C++中的类实例化为对象后,会有一个隐晦的this指针来指出正确的对象(代表实例化后的对象), 所以当你写:
nCount = 1; => 其实它是 this->nCount = 1;
那么,同样的道理,上面类CMyclass的CALLBACK函数 EnumObjectProc也是一个成员函数,所以C++编译器也会为它多准备一个隐藏参数
好,终于说到这里了,问题就出在这个隐藏参数上。 CALLBACK函数是给Windows API调用的, Windows并不经由任何对象调用这个函数,也就无理由传递this指针给callback函数, 这于是导致堆栈中有一个随机变量会成为this指针, 其结果当然是程序的崩溃
所以,如果要把某个函数用作CALLBACK函数,就必须告诉C++编译器,不要放this指针作为该函数的最后一个参数,有两个方法可以做到这一点
1. 就是我们文章开头说的,用全局函数做为CALLBACK函数
2. 使用static成员函数,也就是在函数前面加上static修饰词
第一种作法相当于在 C 语言中使用callback 函数。第二种作法比较接近 OO的精神。
C++中的static成员函数特性是,即使对象还没有产生,static 成员也已经存在(函数或变量都如此)。换句话说对象还没有产生之前你已经可以调用类的static函数或使用类的static变量了。
也就是说,凡声明为static 的东西(不管函数或变量)都并不和对象结合在一起,它们是类的一部分,不属于对象。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
2017-05-29 面试时被问到的问题
2017-05-29 JQuery中查找父元素,子元素,追加元素,插入元素和删除元素 及其他常用方法