以类的成员函数作为Windows callback函数

[注:摘自《深入浅出MFC》第6章浅出MFC程序设计(P.298)]

    首先我要很快地解释一下什么是callback函数。凡是由你设计而却由Windows系统调用的函数,统称为callback函数。这些函数都有一定的类型,以配合Windows的调用操作。

    某些Windows API函数会要求以callback函数作为其参数之一,这些API,例如SetTimer、LineDDA、EnumObjects。通常这种API会在进行某种行为之后或满足某种状态之时调用该callback函数。下面示范的EnumObjects是在发现某个Device Context的GDI object符合我们的指定类型时,调用callback函数。

    好,现在我们要讨论的是,什么函数有资格在C++程序中作为callback函数?这个问题的背后是:C++程序中的callback函数有什么特别的吗?为什么要特别提出讨论?

    是的,特别之处在于,C++编译器为类成员函数多准备了一个隐藏参数(在程序代码中看不到),这使得函数类型与Windows callback函数的默认类型不符。

    假设我们有一个CMyClass如下:

class CMyClass 
{
private:
    int nCount;
    int CALLBACK _export
        EnumObjectsProc(LPSTRlpLogObject, 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;

    基于相同的道理,上例中的EnumObjectdsProc既然是一个成员函数,C++编译器也会为它多准备一个隐藏参数。  

    好,问题就出在这个隐藏参数。callback函数是给Windows调用的,Windows并不借助任何对象调用这个函数,也就没有this指针给callback函数,于是导致堆栈中有一个随机变量会成为指针,而其结果当然是程序的崩溃了。

    要把某个函数用作callback函数,这必须告诉C++编译器,不要让this指针作为该函数的最后一个参数。采用以下两个方法可以做到这一点:

    1.  不要使用类的成员函数(也就是说,要使用全局函数)作为callback函数。

    2.  使用static成员函数。也就是在函数前面加上static修饰词。

    第一种做法相当于在C语言中使用callback函数。第二种做法比较接近OO的精神。

    我想更进一步提醒你的是,C++中的static成员函数特性是,即使对象还没有产生,static成员也已经存在(函数或变量都如此)。换句话说,对象还没有产生之前你已经可以调用类的static函数或使用类的static变量了。请参阅第2章。

    也就是说,凡声明为static的东西(不管函数或变量)都并不和对象结合在一起,它们是类的一部分,不属于对象。


你可能感兴趣的:(windows,windows,mfc,mfc,callback)