回调函数的C++ 封装

在进行软件开发的过程中,常会用到一些声明为CALLBACK 的函数,这些函数就是回调函数。使用
回调函数可以改善软件的结构、提高软件的复用性。比如,在一个规模较大的软件项目中,可以将一些资
源或相对独立的处理模块封装到动态连接库(DLL) 中,然后通过回调函数在不同的场合来使用这些资源
和模块。利用回调函数还可以进行程序间复杂的通信,实现一些通知的功能,在某些场合它是比消息更
合适的一种方式;在一些特殊的情况下,回调函数更有不可替代的作用。Win32 API 中有许多回调函数的
应用,在进行软件设计时也会经常用到这种函数,而有些时候则需要编写自己的回调函数。因此,理解回
调函数的原理并掌握它的基本用法是非常必要的。

C++ 是当代使用最广泛的语言,从嵌入式系统到大型机系统、从LINUX 到WINDOWS , 在大型系统的
编制中,到处都是它的身影。它以高效和易编程性获得了许多资深程序员的信赖。在DirectX Play 开发过
程中,经常需要使用到回调函数,直接使用回调函数显得复杂麻烦,采用用C++ 实现对回调函数的封装,
使回调函数变得方便实用,对于DirectX Play 等编程就显得是非常有意义的。

回调函数简单讲就是一个函数指针。写一个函数,然后把函数地址传递给这个函数指针就可以了。
回调函数的原形对C++ 的成员函数用做回调函数的影响是什么?

编写回调函数简单地说就是函数原形一致。函数的输入参数,输出参数一致是很容易保证的。要注
意调用类型一致性。函数传参数有好几种类型,搞错了传参数的方式,系统必然运行错误。一般来说都
是WINAPI 传参数方式。要注意C++ 的类的成员函数和一般的C 函数的区别。C++ 类采用this 规则传
递函数。在使用类的成员函数作为回调函数,要求该成员函数被声名为静态成员函数,并且注意函数声
名的时候要同时声明好参数传递规则。

1  静态成员函数调用非静态成员函数

由于静态成员函数的存在是在类的实例产生之前,在每本C++ 的书中都提到,静态成员函数只可以
调用静态成员函数,并使用类的静态成员。但如果这样,写一个全部都是静态成员的类,似乎失去了C++
类的大部分优点。它在完美的封装的同时,使得类不可以实例化。比如,对窗口消息处理的回调函数编
程,系统中有无数个窗口,如果写一个全部都是静态成员的类,是无法描述这些窗口的。怎么在静态成员
函数中使用类的非静态成员呢?

可以给静态成员函数传入参数,而且一般设计的比较好的回调函数都提供一个数据块指针作为传入

参数,这时候就可以采用这种结构。
回调函数类型void Fun(void 3 pData ,UINT uMsg)
class CFun
{
 static void StaticFun(void 3 pData ,UINT uMsg)
 {
      CFun 3 pThisObject = (CFun 3 ) pData ;
      pThisObject -> Fun(uMsg) ; / / (1) 
    } 
    void Fun(UINT nMsg) 
    { 
    }
};
如此,回调函数就和C++ 类形成了一个完美的体系。由于(1) 处使用的是CFun 的成员函数,该类生

成的实例可以访问类的其他任何函数成员和数据成员。这样处理回调函数只是把C++ 类和回调函数连
接起来了。但还没有发挥出C++ 的全部优势。现在把FUN 的函数声明改一下。

2  函数的分发再处理

class CFun   

{

    static void StaticFun(void 3 pData ,UINT uMsg) ;

    protected:

    vitual void Fun(UINT nMsg) ;

};     

同时写一个派生类,   

class CSubFun : public CFun    

{        

    void Fun(UINT nMsg) ;      

    void SubFun1() ;        

 void SubFun2() ;

  ..

}

同时实现该函数如下

voidCSubFun::Fun(UINTnMsg)

{

    switch(nMsg)

 {

 case 1:SubFun1() ; break;

 case 2:SubFun2() ; break;

   ...

    }

}

 

现在可以看到由于虚函数的使用,C ++ 的派生类也具有了函数处理的能力。也就是说,实现了一个
对回调函数封装的类,可以在基类中定义一些实现处理,但如果基类对回调函数的传入参数不符合要求,
可以随时按照我们的要求修改它。

按照要求实现对基类处理的修改,上面的处理是有缺陷的。没有调用到基类的处理函数,这样丢弃
了基类的处理例程是非常不可取的。可以重新做一下类的设计。修改派生类的函数Fun 如下:
BOOL CSubFun: :Fun(UINT nMsg)

{

    bHandle = FALSE; 
    switch(nMsg) 
    {
        case1:

                bHandle = SubFun1() ;

                break;

        case 2:

                bHandle = SubFun2() ;

                 break;

                 . . . 
    } 
    if (bHandle)

         return TRUE;

     else

         returnCFun::Fun(nMsg);
}

 

至此,C ++ 的类终于和回调函数完美的结合了。所有C ++ 的组合继承等等的优点全部重现。派生
类可以决定是否处理该消息,甚至在该消息处理后继续要求基类去处理。

 

3  回调函数分发的简化

其次,如果nMsg 的数目是非常庞大的,那么函数码函数处理看起来就不那么愉快了。可以定义几个
宏来处理:

  # define DECLARE-FUN-TABLE() /

    virtual void Fun(UINT nMsg) ; 

        # defineBEGIN-FUN-TABLE(theClass,baseClass)

      BOOL theClass: :Fun(UINT nMsg){ /

      bHandle = FALSE; /

      switch(nMsg)

              { 

                      # define ON-FUN(nMsg,Function) /     

                                  case nMsg: bHandle = Funcion() ;break;      

                     #define END-FUN-TABLE(theClass,baseClass) }/

                      if (bHandle)

                                         return TRUE; /    

                               else

                                         return baseClass::Fun(nMsg);

              }         

 

如此一来,基类不变,派生类就改变如下:

    class CSubFun : public CFun

    {

        BOOL SubFun1() ;

        BOOL SunFun2() ;

        . . .

        DECLARE-FUN-TABLE()

     } 

     BEGIN-FUN-TABLE(CSubFun,CFun)

     ON-FUN(1 ,SubFun1)

     ON-FUN(2,SubFun2)

     END-FUN-TABLE(CSubFun ,CFun)

 

这种类似的宏结构在MFC 的消息映射机制中可以看到,但它没有采用虚函数实现。MFC 认为虚拟机
制这种效率太低了,WINDOW 有数千个消息,窗口消息回调函数的调用是非常频繁的。但由于上述回调
函数的调用频率比消息映射机制少的多,消息的数目也少的多,采用这种机制就可以了。如果想继续提
升这种封装机制,可以参考以下MFC 的消息映射机制和ATL 的函数分发机制。

 

  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
C++中,回调函数通常是通过函数指针或者函数对象来实现的。下面分别介绍这两种方式的写法: 1. 函数指针方式 首先,定义一个函数指针类型,该类型定义了回调函数的参数类型和返回值类型: ``` typedef void (*CallbackFunc)(int); ``` 然后,在需要使用回调函数的地方,通过一个指向回调函数的函数指针来执行回调函数: ``` void DoSomething(CallbackFunc callback) { // 执行某些操作 int value = 10; // 调用回调函数 callback(value); } ``` 在调用 `DoSomething` 函数时,将一个函数指针作为参数传递进去,该函数指针指向的就是回调函数。在 `DoSomething` 函数内部,通过该函数指针调用回调函数。 2. 函数对象方式 使用函数对象实现回调函数的方式与使用函数指针类似,只不过回调函数封装在一个类的成员函数中。首先,定义一个回调函数类,并在其中定义回调函数的成员函数: ``` class Callback { public: void operator()(int value) { // 执行回调函数的操作 } }; ``` 在需要使用回调函数的地方,通过一个回调函数对象来执行回调函数: ``` void DoSomething(Callback& callback) { // 执行某些操作 int value = 10; // 调用回调函数 callback(value); } ``` 在调用 `DoSomething` 函数时,将一个回调函数对象作为参数传递进去,然后在 `DoSomething` 函数内部通过该函数对象调用回调函数的成员函数。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值