回调函数中调用类中的非静态成員变量或非静态成员函数
【问题1】如何在类中封装回调函数
a.回调函数只能是全局的或是静态的。
b.全局函数会破坏类的私有成员函数怎么調用封装性故不予采用。
c.静态函数只能访问类的私有成员函数怎么调用静态成员不能访问类中非静态成员。
【问题2】如何让静态函数訪问类的私有成员函数怎么调用非静态成员
回调函数是基于C编程的Windows SDK的技术,不是针对C++的程序员可以将一个C函数直接作为回调函数,但昰如果试图直接使用C++的成员函数作为回调函数将发生错误甚至编译就不能通过。
普通的C++成员函数都隐含了一个传递函数作为参数亦即“this”指针,C++通过传递一个指向自身的指针给其成员函数从而实现程序函数可以访问C++的数据成员这也可以理解为什么C++类的私有成员函数怎麼调用多个实例可以共享成员函数但是确有不同的数据成员。由于this指针的作用使得将一个CALLBACK型的成员函数作为回调函数安装时就会因为隐含的this指针使得函数参数个数不匹配,从而导致回调函数安装失败
这样从理论上讲,C++类的私有成员函数怎么调用成员函数是不能当作回调函数的但我们在用C++编程时总希望在类内实现其功能,即要保持封装性如果把回调函数写作普通函数有诸多不便。经过网上搜索和自己研究发现了几种巧妙的方法,可以使得类成员函数当作回调函数使用
第一个参数为指向线程标识符的指针。
第二个参数用来设置线程屬性
第三个参数是线程运行函数的起始地址,即回调函数
最后一个参数是运行函数的参数。
这里我们只关注第三个参数start_run它是一个函數指针,指向一个以void*为参数返回值为void*的函数,这个函数被当作线程的回调函数使用线程启动后便会执行该函数的代码。
方法一:回调函数为普通函数但在函数体内执行成员函数
类MyClass需要在自己内部开辟一个子线程来执行成员函数func()中的代码,子线程通过调用startThread()成员函数来启動这里将回调函数callback写在了类外面,传递的参数是一个指向MyClass对象的指针(在pthrad_create()中由第4个参数this指定)回调函数经过强制转换把void*变为MyClass*,然后再调用arg->func()執行子线程的代码
这样做的原理是把当前对象的指针当作参数先交给一个外部函数,再由外部函数调用类成员函数以外部函数作为回調函数,但执行的是成员函数的功能这样相当于在中间作了一层转换。缺点是回调函数在类外影响了封装性,这里把callback()限定为static防止在其它文件中调用此函数。
方法二:回调函数为类内静态成员函数在其内部调用成员函数
在方法一上稍作更改,把回调函数搬到类MyClass里这樣就保持了封装性。代码如下:
类MyClass有了1个静态数据成员CurMy和1个静态成员函数callbackCurMy用来存储一个对象的指针,充当方法一中回调函数的参数argcallback当莋回调函数,执行CurMy->func()的代码每次建立线程前先要调用setCurMy()来让CurMy指向当前自己。这个方法的好处时封装性得到了很好的保护MyClass对外只公开一个接ロstartThread(),子线程代码和回调函数都被设为私有外界不可见。另外没有占用callback的参数可以从外界传递参数进来。但每个对象启动子线程前一定偠注意先调用setCurMy()让CurMy正确的指向自身否则将为其它对象开启线程,这样很引发很严重的后果
方法三:对成员函数进行强制转换,当作回调函数
这个方法是原理是MyClass::func最终会转化成 void func(MyClass *this); 也就是说在原第一个参数前插入指向对象本身的this指针。可以利用这个特性写一个非静态类成员方法來直接作为线程回调函数对编译器而言,void (MyClass::*FUNC1)()和void* (*FUNC)(void*)这两种函数指针虽然看上去很不一样但他们的最终形式是相同的,因此就可以把成员函数指针强制转换成普通函数的指针来当作回调函数在建立线程时要把当前对象的指针this当作参数传给回调函数(成员函数func),这样才能知道线程昰针对哪个对象建立的方法三的封装性比方法二更好,因为不涉及多个对象共用一个静态成员的问题每个对象可以独立地启动自己的線程而不影响其它对象。