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 的东西(不管函数或变量)都并不和对象结合在一起,它们是类的一部分,不属于对象。

 

posted on 2024-05-29 15:31  新西兰程序员  阅读(5)  评论(0编辑  收藏  举报