中类成员函数作为回调函数

回调函数是基于C编制程序的Windows
SDK的技能,不是本着C++的,程序员能够将三个C函数直接作为回调函数,不过1旦打算直接选择C++的积极分子函数作为回调函数将发生错误,甚至编写翻译就不能够透过。 

注:与tr1::function对象结合使用,能收获更加好的功力,详情见http://blog.csdn.net/this_capslock/article/details/38564719

一般而言的C++成员函数都饱含了八个传递函数作为参数,亦即“this”指针,C++通
过传递2个针对性自个儿的指针给其成员函数从而达成程序函数能够访问C++的数码成员。那也足以领略为啥C++类的八个实例可以共享成员函数不过确有不相同的
数据成员。由于this指针的职能,使得将三个CALLBACK型的分子函数作为回调函数安装时就会因为含有的this指针使得函数参数个数不协作,从而
导致回调函数安装退步。

 

如此从理论上讲,C++类的成员函数是不可能当做回调函数的。但大家在用C++编程时总希望在类内实现其成效,即要保持封装性,假诺把回调函数写作普通函数有诸多不便。经过网上检索和融洽钻探,发现了二种高超的秘诀,能够使得类成员函数当作回调函数使用。

回调函数是根据C编程的Windows
SDK的技术,不是针对性C++的,程序员能够将1个C函数直接作为回调函数,不过只要打算直接使用C++的成员函数作为回调函数将发生错误,甚至编写翻译就不能通过。 

那里运用Linux
C++中线程成立函数pthread_create举例,其原型如下:

万般的C++成员函数都包含了三个传递函数作为参数,亦即“this”指针,C++通过传递一个对准自个儿的指针给其成员函数从而完毕程序函数能够访问C++的数量成员。那也得以精通为何C++类的四个实例能够共享成员函数可是确有分歧的数码成员。由于this指针的功用,使得将3个CALLBACK型的分子函数作为回调函数安装时就会因为含有的this指针使得函数参数个数不相称,从而导致回调函数安装战败。

 

这么从理论上讲,C++类的分子函数是不能够看做回调函数的。但大家在用C++编制程序时总希望在类内实现其意义,即要保持封装性,假使把回调函数写作普通函数有不便。经过网上寻找和友爱商讨,发现了两种高超的措施,能够使得类成员函数当作回调函数使用。

[cpp] view
plain
copyprint?图片 1图片 2

这边运用Linux C++中线程创制函数pthread_create举例,其原型如下:

  1. int pthread_create( pthread_t *restrict tidp , const pthread_attr_t *restrict attr , void* (*start_rtn)(void*) , void *restrict arg );  

 

首先个参数为指向线程标识符的指针。

[cpp] view
plain
 copy

其次个参数用来设置线程属性。

 

其八个参数是线程运维函数的苗头地址,即回调函数。

 print?图片 3图片 4

最后一个参数是运作函数的参数。

  1. int pthread_create( pthread_t *restrict tidp , const pthread_attr_t *restrict attr , void* (*start_rtn)(void*) , void *restrict arg );  

此地大家只关怀第多少个参数start_run,它是贰个函数指针,指向1个以void*为参数,重返值为void*的函数,那些函数被当作线程的回调函数使用,线程运维后便会执行该函数的代码。

 

格局一:回调函数为平常函数,但在函数体内实施成员函数

第三个参数为指向线程标识符的指针。

见之下代码:

第三个参数用来设置线程属性。

[cpp] view
plain
copyprint?图片 5图片 6

其三个参数是线程运维函数的序曲地址,即回调函数。

  1. class MyClass  
  2. {  
  3.     pthread_t TID;  
  4. public:  
  5.     void func()  
  6.     {  
  7.         //子线程执行代码  
  8.     }  
  9.   
  10.     bool startThread()  
  11.     {//运维子线程  
  12.         int ret = pthread_create( &TID , NULL , callback , this );  
  13.         if( ret != 0 )  
  14.             return false;  
  15.         else  
  16.             return true;  
  17.     }  
  18. };  
  19.   
  20. static void* callback( void* arg )  
  21. {//回调函数  
  22.     ((MyClass*)arg)->func();调用成员函数  
  23.     return NULL;  
  24. }  
  25.   
  26. int main()  
  27. {  
  28.     MyClass a;  
  29.     a.startThread();  
  30. }  

最终二个参数是运转函数的参数。

类MyClass供给在和谐之中开辟三个子线程来推行成员函数func()中的代码,
子线程通过调用startThread()成员函数来运行。那里将回调函数callback写在了类外面,传递的参数是二个针对性MyClass对象的指针
(在pthrad_create()中由第四个参数this钦点),回调函数经过强制转换把void*变为MyClass*,然后再调用
arg->func()执行子线程的代码。

 

如此那般做的规律是把当前目的的指针当作参数先付给2个表面函数,再由外部函数调用类成员
函数,以外部函数作为回调函数,但实施的是成员函数的功能,那样也就是在中间作了一层转换。缺点是回调函数在类外,影响了封装性,那里把
callback()限定为static,幸免在其他文件中调用此函数。

那边大家只关怀第7个参数start_run,它是一个函数指针,指向三个以void*为参数,重回值为void*的函数,那一个函数被当作线程的回调函数使用,线程运行后便会执行该函数的代码。

方式二:回调函数为类内静态成员函数,在个中间调用成员函数

 

在措施壹上稍作更改,把回调函数搬到类MyClass里,那样就保险了封装性。代码如下:

主意一:回调函数为平时函数,但在函数体内实施成员函数

[cpp] view
plain
copyprint?图片 7图片 8

见之下代码:

  1. class MyClass  
  2. {  
  3.     static MyClass* CurMy;//存款和储蓄回调函数调用的目的  
  4.     static void* callback(void*);//回调函数  
  5.     pthread_t TID;  
  6.     void func()  
  7.     {  
  8.         //子线程执行代码  
  9.     }  
  10.       
  11.     void setCurMy()  
  12.     {//设置当前指标为回调函数调用的靶子  
  13.         CurMy = this;  
  14.     }  
  15. public:  
  16.     bool startThread()  
  17.     {//运维子线程  
  18.         setCurMy();  
  19.         int ret = pthread_create( &TID , NULL , MyClass::callback , NULL );  
  20.         if( ret != 0 )  
  21.             return false;  
  22.         else  
  23.             return true;  
  24.     }  
  25. };  
  26. MyClass* MyClass::CurMy = NULL;  
  27. void* MyClass::callback(void*)  
  28. {  
  29.     CurMy->func();  
  30.     return NULL;  
  31. }  
  32.   
  33. int main()  
  34. {  
  35.     MyClass a;  
  36.     a.startThread();  
  37. }  

[cpp] view
plain
 copy


MyClass有了3个静态数据成员CurMy和1个静态成员函数callback。CurMy用来储存一个对象的指针,充当方法第一中学回调函数的参数
arg。callback当作回调函数,执行CurMy->func()的代码。每一遍建立线程前先要调用setCurMy()来让CurMy指向当
前本身。

 

这几个主意的裨益时封装性获得了很好的保卫安全,MyClass对外只公开八个接口
startThread(),子线程代码和回调函数都被设为私有,外界不可知。此外未有据为己有callback的参数,能够从外边传递参数进来。但各类对象
运营子线程前自然要小心先调用setCurMy()让CurMy正确的针对性自个儿,否则将为其余对象开启线程,那样很吸引很严重的后果。

 print?图片 9图片 10

主意叁:对成员函数进行强制转换,当作回调函数

  1. class MyClass  
  2. {  
  3.     pthread_t TID;  
  4. public:  
  5.     void func()  
  6.     {  
  7.         //子线程执行代码  
  8.     }  
  9.   
  10.     bool startThread()  
  11.     {//运营子线程  
  12.         int ret = pthread_create( &TID , NULL , callback , this );  
  13.         if( ret != 0 )  
  14.             return false;  
  15.         else  
  16.             return true;  
  17.     }  
  18. };  
  19.   
  20. static void* callback( void* arg )  
  21. {//回调函数  
  22.     ((MyClass*)arg)->func();调用成员函数  
  23.     return NULL;  
  24. }  
  25.   
  26. int main()  
  27. {  
  28.     MyClass a;  
  29.     a.startThread();  
  30. }  

代码如下:

 

[cpp] view
plain
copyprint?图片 11图片 12

类MyClass供给在和谐内部开辟叁个子线程来推行成员函数func()中的代码,子线程通过调用startThread()成员函数来运转。这里将回调函数callback写在了类外面,传递的参数是2个针对MyClass对象的指针(在pthrad_create()中由第伍个参数this钦命),回调函数经过强制转换把void*变为MyClass*,然后再调用arg->func()执行子线程的代码。

  1. class MyClass  
  2. {  
  3.     pthread_t TID;  
  4.     void func()  
  5.     {  
  6.         //子线程执行代码  
  7.     }  
  8. public:  
  9.     bool startThread()  
  10.     {//运转子线程  
  11.         typedef void* (*FUNC)(void*);//定义FUNC类型是3个针对函数的指针,该函数参数为void*,重临值为void*  
  12.         FUNC callback = (FUNC)&MyClass::func;//强制转换func()的种类  
  13.         int ret = pthread_create( &TID , NULL , callback , this );  
  14.         if( ret != 0 )  
  15.             return false;  
  16.         else  
  17.             return true;  
  18.     }  
  19. };  
  20.   
  21. int main()  
  22. {  
  23.     MyClass a;  
  24.     a.startThread();  
  25. }  

那般做的规律是把方今目的的指针当作参数先交付3个外表函数,再由外部函数调用类成员函数,以外部函数作为回调函数,但实施的是成员函数的效用,那样相当于在中间作了1层转换。缺点是回调函数在类外,影响了封装性,那里把callback()限定为static,防止在别的文件中调用此函数。

其一办法是规律是,MyClass::func最后会转化成
void func(MyClass *this);
相当于说在原第伍个参数前插入指向对象自小编的this指针。能够应用那个特性写三个非静态类成员方法来直接当做线程回调函数。对编写翻译器而言,void
(MyClass::*FUNC1)()和void*
(*FUNC)(void*)那三种函数指针固然看上去很不壹样,但他俩的尾声情势是均等的,因而就足以把成员函数指针强制转换来普通函数的指针来作为回
调函数。在创制线程时要把当前目的的指针this当作参数字传送给回调函数(成员函数func),那样才能知道线程是针对性哪个目的建立的。

 

措施3的封装性比办法二越来越好,因为不涉及多少个对象共用三个静态成员的难点,每一个对象足以独自地开发银行本人的线程而不影响别的对象。

形式贰:回调函数为类内静态成员函数,在在那之中间调用成员函数

近年来就列出那几个办法,今后发现更加好的再来补充,over

在点子壹上稍作更改,把回调函数搬到类MyClass里,那样就有限援救了封装性。代码如下:

 

[cpp] view
plain
 copy

 

 print?图片 13图片 14

  1. class MyClass  
  2. {  
  3.     static MyClass* CurMy;//存款和储蓄回调函数调用的对象  
  4.     static void* callback(void*);//回调函数  
  5.     pthread_t TID;  
  6.     void func()  
  7.     {  
  8.         //子线程执行代码  
  9.     }  
  10.       
  11.     void setCurMy()  
  12.     {//设置当前指标为回调函数调用的指标  
  13.         CurMy = this;  
  14.     }  
  15. public:  
  16.     bool startThread()  
  17.     {//运营子线程  
  18.         setCurMy();  
  19.         int ret = pthread_create( &TID , NULL , MyClass::callback , NULL );  
  20.         if( ret != 0 )  
  21.             return false;  
  22.         else  
  23.             return true;  
  24.     }  
  25. };  
  26. MyClass* MyClass::CurMy = NULL;  
  27. void* MyClass::callback(void*)  
  28. {  
  29.     CurMy->func();  
  30.     return NULL;  
  31. }  
  32.   
  33. int main()  
  34. {  
  35.     MyClass a;  
  36.     a.startThread();  
  37. }  

类MyClass有了二个静态数据成员CurMy和一个静态成员函数callback。CurMy用来存款和储蓄1个目的的指针,充当方法一中回调函数的参数arg。callback当作回调函数,执行CurMy->func()的代码。每一遍建立线程前先要调用setCurMy()来让CurMy指向当前协调。

 

本条点子的功利时封装性得到了很好的护卫,MyClass对外只公开四个接口startThread(),子线程代码和回调函数都被设为私有,外界不可知。其它未有占用callback的参数,能够从外侧传递参数进来。但各类对象运行子线程前必然要注意先调用setCurMy()让CurMy正确的对准自个儿,不然将为其它对象开启线程,这样很吸引很严重的结果。

 

办法叁:对成员函数实行强制转换,当作回调函数

代码如下:

 

[cpp] view
plain
 copy

 

 print?图片 15图片 16

  1. class MyClass  
  2. {  
  3.     pthread_t TID;  
  4.     void func()  
  5.     {  
  6.         //子线程执行代码  
  7.     }  
  8. public:  
  9.     bool startThread()  
  10.     {//启动子线程  
  11.         typedef void* (*FUNC)(void*);//定义FUNC类型是1个对准函数的指针,该函数参数为void*,重返值为void*  
  12.         FUNC callback = (FUNC)&MyClass::func;//强制转换func()的种类  
  13.         int ret = pthread_create( &TID , NULL , callback , this );  
  14.         if( ret != 0 )  
  15.             return false;  
  16.         else  
  17.             return true;  
  18.     }  
  19. };  
  20.   
  21. int main()  
  22. {  
  23.     MyClass a;  
  24.     a.startThread();  
  25. }  

其一法子是原理是,MyClass::func最后会转化成 void func(MyClass *this);
也正是说在原第3个参数前插入指向对象自作者的this指针。能够采纳这些特点写多个非静态类成员方法来直接当做线程回调函数。对编写翻译器而言,void
(MyClass::*FUNC1)()和void*
(*FUNC)(void*)这三种函数指针即便看上去很不均等,但她俩的末尾格局是1律的,由此就足以把成员函数指针强制转换成普通函数的指针来作为回调函数。在建立线程时要把近来指标的指针this当作参数字传送给回调函数(成员函数func),那样才能明了线程是指向哪个目的建立的。

 

办法三的封装性比办法二更加好,因为不涉及多少个指标共用二个静态成员的题材,每个对象能够单独地开发银行本身的线程而不影响其余对象。

 

一时半刻就列出那么些措施,以往发现更加好的再来补充,over

http://blog.csdn.net/this\_capslock/article/details/17001003


4楼 eziowayne 2015-04-17 15:38发表 [回复]
图片 17

个人推举第三种方法,第二种办法适用面窄,第两种是不当的格局,千万不能如此用。

3楼 __JacHan 2014-04-28 14:44发表 [回复]
图片 18

用的时候用楼主的章程三实际上调不通,论坛里面有人如此表明的:为了促成回调,大家无法不把this指针给转换掉!可为了在该函数中得以平素操作该类中的成员,大家亟须保留this指针!所以那是争执的,通过更换指针的手腕是不能够完结目标的!
来自:http://bbs.csdn.net/topics/360051927
还是用tr1::function/bind实现了