回调函数中调用类中的非静态成员变量或非静态成员函数程序员
【问题1】如何在类中封装回调函数?编程
【答】:
a.回调函数只能是全局的或是静态的。
b.全局函数会破坏类的封装性,故不予采用。
c.静态函数只能访问类的静态成员,不能访问类中非静态成员。
【问题2】如何让静态函数访问类的非静态成员?
【解决方案】:函数
声明一静态函数a(),将类实例对象指针作为参数传入。如:
class A()
{
static void a(A *); //静态函数
void b(); //非静态函数
}
void A::a(A * pThis)
{
pThis->b(); //静态函数中调用非静态函数
}
【问题3】回调函数中如何访问非静态成员?
因为回调函数每每有固定定义,并不接受 A * pThis 参数
如:CALLBACK MyTimerProc(HWND hwnd,UINT uMsg,UINT idEvent,DWORD dwTime);
【解决方案1】:本方案当遇到有多个类实例对象时会有问题。缘由是pThis指针只能指向一个对象。
class A()
{
static void a(); //静态回调函数
void b(); //非静态函数
static A * pThis; //静态对象指针
}
A * A::pThis=NULL;
A::A() //构造函数中将this指针赋给pThis,使得回调函数能经过pThis指针访问本对象
{
pThis=this;
}
void A::a()
{
if (pThis==NULL) return;
pThis->b(); //回调函数中调用非静态函数
}
【解决方案2】:本方案解决多个类实例对象时方案1的问题。用映射表存全部对象地址,每一个对象保存本身的ID号。
typedef CMap<UINT,UINT,A*,A*> CAMap;
class A()
{
static void a(); //静态回调函数
void b(); //非静态函数
int m_ID; //本对象在列表中的ID号
static int m_SID; //静态当前对象ID (须要时,将m_ID赋值给m_SID以起到调用本对象函数的功能)
static CAMap m_Map; //静态对象映射表
}
CAMap A::m_Map;
int A::m_SID=0;
A::A() //构造函数中将this指针赋给pThis,使得回调函数能经过pThis指针访问本对象
{
if(m_Map.IsEmpty())
{
m_ID=1;
}
else
{
m_ID=m_Map.GetCount()+1;
}
m_Map.SetAt( m_ID, this );
}
void A::a()
{
if (m_Map.IsEmpty()) return;
A * pThis=NULL;
if(m_Map.Lookup(m_SID,pThis))
{
pThis->b(); //回调函数中调用非静态函数
};
}this
=================================spa
回调函数是基于C编程的Windows SDK的技术,不是针对C++的,程序员能够将一个C函数直接做为回调函数,可是若是试图直接使用C++的成员函数做为回调函数将发生错误,甚至编译就不能经过。 .net
普通的C++成员函数都隐含了一个传递函数做为参数,亦即“this”指针,C++经过传递一个指向自身的指针给其成员函数从而实现程序函数能够访问C++的数据成员。这也能够理解为何C++类的多个实例能够共享成员函数可是确有不一样的数据成员。因为this指针的做用,使得将一个CALLBACK型的成员函数做为回调函数安装时就会由于隐含的this指针使得函数参数个数不匹配,从而致使回调函数安装失败。线程
这样从理论上讲,C++类的成员函数是不能看成回调函数的。但咱们在用C++编程时总但愿在类内实现其功能,即要保持封装性,若是把回调函数写做普通函数有诸多不便。通过网上搜索和本身研究,发现了几种巧妙的方法,可使得类成员函数看成回调函数使用。指针
这里采用Linux C++中线程建立函数pthread_create举例,其原型以下:rest
类MyClass须要在本身内部开辟一个子线程来执行成员函数func()中的代码,子线程经过调用startThread()成员函数来启动。这里将回调函数callback写在了类外面,传递的参数是一个指向MyClass对象的指针(在pthrad_create()中由第4个参数this指定),回调函数通过强制转换把void*变为MyClass*,而后再调用arg->func()执行子线程的代码。code
这样作的原理是把当前对象的指针看成参数先交给一个外部函数,再由外部函数调用类成员函数,之外部函数做为回调函数,但执行的是成员函数的功能,这样至关于在中间做了一层转换。缺点是回调函数在类外,影响了封装性,这里把callback()限定为static,防止在其它文件中调用此函数。
方法二:回调函数为类内静态成员函数,在其内部调用成员函数
在方法一上稍做更改,把回调函数搬到类MyClass里,这样就保持了封装性。代码以下:
这个方法的好处时封装性获得了很好的保护,MyClass对外只公开一个接口startThread(),子线程代码和回调函数都被设为私有,外界不可见。另外没有占用callback的参数,能够从外界传递参数进来。但每一个对象启动子线程前必定要注意先调用setCurMy()让CurMy正确的指向自身,不然将为其它对象开启线程,这样很引起很严重的后果。
方法三:对成员函数进行强制转换,看成回调函数
代码以下:
方法三的封装性比方法二更好,由于不涉及多个对象共用一个静态成员的问题,每一个对象能够独立地启动本身的线程而不影响其它对象。