回调函数的C++ 封装
2008-09-05 18:35
260 查看
在进行软件开发的过程中,常会用到一些声明为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 的函数分发机制。
回调函数可以改善软件的结构、提高软件的复用性。比如,在一个规模较大的软件项目中,可以将一些资
源或相对独立的处理模块封装到动态连接库(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 的函数分发机制。
相关文章推荐
- C++回调函数封装成C#委托测试
- C++ 类中封装Win32API的回调函数
- 回调函数的C++ 封装(非静态成员函数的回调函数实现方法)
- c++ 回调函数封装
- C++远征之封装篇(笔记)
- 从C++对象内存布局和构造过程来具体分析C++中的封装、继承、多态
- 将sqlite3编译成给WINCE使用的DLL,并封装成C++的步骤(使用VS2005)
- MySQL的C++封装
- C++远征之封装篇(下)
- MySql C++调用库Connector/c++编译 和 接口封装【二】Connector/c++编译
- c++实现的一对锁的封装(挺有意思的)
- c++用模板类封装了队列 提高了可重用性
- c++回调函数(下)
- 基于wke封装的duilib的webkit浏览器控件,可以c++与js互交,源码及demo下载地址
- C++学习笔记:回调函数使用
- c#调用c++封装dll 参数为字符串输出函数的解决办法
- IP层的封装(Java的InetAddress类的C++实现)
- C++ 配置文件类的封装
- C/C++之回调函数
- 自己动手封装VxWorks下C++基础库