类成员函数回调
2016-04-30 18:03
260 查看
如果试图直接使用C++的成员函数作为回调函数将发生错误,甚至编译就不能通过。其错误是普通的C++成员函数都隐含了一个传递函数作为参数,亦即“this”指针,C++通过传递this指针给其成员函数从而实现程序函数可以访问C++的数据成员。这也可以理解为什么C++类的多个实例可以共享成员函数却-有不同的数据成员。由于this指针的作用,使得将一个CALL-BACK型的成员函数作为回调函数安装时就会因为隐含的this指针使得函数参数个数不匹配,从而导致回调函数安装失败。要解决这一问题的关键就是不让this指针起作用,通过采用以下两种典型技术可以解决在C++中使用回调函数所遇到的问题。这种方法具有通用性,适合于任何C++。
1). 不使用成员函数,为了访问类的成员变量,可以使用友元操作符(friend),在C++中将该函数说明为类的友元即可。 (将函数声明为类的友元,在调用类成员变量时,要通过实例的对象来调用。而且这种方法会破环类的封装性)
2). 使用静态成员函数,静态成员函数不使用this指针作为隐含参数,这样就可以作为回调函数了。静态成员函数具有两大特点:其一,可以在没有类实例的情况下使用;其二,只能访问静态成员变量和静态成员函数,不能访问非静态成员变量和非静态成员函数。由于在C++中使用类成员函数作为回调函数的目的就是为了访问所有的成员变量和成员函数,如果做不到这一点将不具有实际意义。解决的办法也很简单,就是使用一个静态类指针作为类成员,通过在类创建时初始化该静态指针,如pThis=this(可以在构造函数中完成此步转换),然后在回调函数中通过该静态指针就可以访问所有成员变量和成员函数了。
这种处理办法适用于只有一个类实例的情况,因为多个类实例将共享静态类成员和静态成员函数,这就导致静态指针指向最后创建的类实例。为了避免这种情况,可以使用回调函数的一个参数来传递this指针,从而实现数据成员共享。这种方法稍稍麻烦,这里就不再赘述
我们知道回调函数只能是全局的或是静态的。
全局函数会破坏类的封装性,故不予采用。
而静态函数只能访问类的静态成员,不能访问类中非静态成员。
2).回调函数(静态)中访问非静态成员
由于回调函数往往有固定定义,并不接受 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(); //回调函数中调用非静态函数
};
}
1). 不使用成员函数,为了访问类的成员变量,可以使用友元操作符(friend),在C++中将该函数说明为类的友元即可。 (将函数声明为类的友元,在调用类成员变量时,要通过实例的对象来调用。而且这种方法会破环类的封装性)
2). 使用静态成员函数,静态成员函数不使用this指针作为隐含参数,这样就可以作为回调函数了。静态成员函数具有两大特点:其一,可以在没有类实例的情况下使用;其二,只能访问静态成员变量和静态成员函数,不能访问非静态成员变量和非静态成员函数。由于在C++中使用类成员函数作为回调函数的目的就是为了访问所有的成员变量和成员函数,如果做不到这一点将不具有实际意义。解决的办法也很简单,就是使用一个静态类指针作为类成员,通过在类创建时初始化该静态指针,如pThis=this(可以在构造函数中完成此步转换),然后在回调函数中通过该静态指针就可以访问所有成员变量和成员函数了。
这种处理办法适用于只有一个类实例的情况,因为多个类实例将共享静态类成员和静态成员函数,这就导致静态指针指向最后创建的类实例。为了避免这种情况,可以使用回调函数的一个参数来传递this指针,从而实现数据成员共享。这种方法稍稍麻烦,这里就不再赘述
我们知道回调函数只能是全局的或是静态的。
全局函数会破坏类的封装性,故不予采用。
而静态函数只能访问类的静态成员,不能访问类中非静态成员。
2).回调函数(静态)中访问非静态成员
由于回调函数往往有固定定义,并不接受 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(); //回调函数中调用非静态函数
};
}
相关文章推荐
- Codeforces Round #349 (Div. 2)-A. Pouring Rain(数学)
- Leetcode - Excel Sheet Column Title
- Worm
- bootstrap ch2清除浮动+12
- NBUT1461 数字整除(大数处理,减法、除法)
- nyoj_106 背包问题
- 微信公众号第三方开发之四回调url中获取授权方的授权信息以及基本信息
- NSLog的实现
- 链表实验
- LightOJ - 1433 Minimum Arc Distance (数学几何)求圆上两点间的弧长
- 当世界对你说“不”
- 阿里的味道
- Head First Java设计模式思维导图总结
- dubbo 教程
- bootstrap ch2清除浮动
- DOM(一)
- NYOJ 832 合并游戏(dp状态压缩)
- Double Shortest Paths 网络流
- 如何通过问卷调查与数据分析创建用户模型
- XMG 通讯页面的创建