回调函数中调用类中的非静态成员变量或非静态成员函数

有关这方面的问题,首先说一点:

回调函数必须是静态成员函数或者全局函数来实现回调函数,大概原因是普通的C++成员函数都隐含了一个函数参数,即this指针,C++通过传递this指针给成员函数从而实现函数可以访问类的特定对象的数据成员。由于this指针的原因,使得一个普通成员函数作为回调函数时就会因为隐含的this指针问题使得函数参数个数不匹配,从而导致回调函数编译失败。

基于上面的理论,如何在类中封装回调函数呢?

回调函数只能是全局函数或者静态成员函数,但是由于全局函数会破坏封装性,所以只能用静态成员函数作为回调函数,这样才能在类中封装回调函数。

但是如果有这样的需求,当触发某一个事件的时候,我想访问类的普通成员函数和普通成员变量,怎么办?首先明确一点,普通的成员函数不能作为回调函数,所以不能在事件触发后直接回调普通成员函数。那么只能有一种办法,当事件触发后,回调类的静态成员函数,然后想办法通过类的静态成员函数访问普通成员函数和普通的成员变量。

注意:类的静态成员函数由于没有this指针,是不能直接访问类的普通成员变量的,也不能访问类的普通成员函数。这里说的是想办法。

1. 如何让静态函数访问类的非静态成员

声明一静态函数a(),将类实例对象指针做为参数传入

class A()
{
    static void a(A *pThis); //静态函数
    void b();        //非静态函数 
};  

void A::a(A *pThis)
{
    pThis->b(); //静态函数中调用非静态函数 
}
  1. 回调函数中访问非静态成员(这里的回调函数只能是类的静态成员函数)

由于回调函数往往有固定定义,并不接受 A * pThis 参数,如:CALLBACK MyTimerProc(HWND hwnd,UINT uMsg,UINT idEvent,DWORD dwTime);

【解决方案一】不把对象指针作为静态成员函数的参数,而是作为类的静态成员变量。这样在静态成员函数(已经作为回调函数)中也能够直接访问这个对象指针(通过对象指针操作普通成员变量和普通成员函数)。

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(); //回调函数中调用非静态函数 
}

本方案当遇到有多个类实例对象时会有问题。原因是pThis指针只能指向一个对象,所以不能调用不同对象的b()函数,换而言之还是不能够真正的在回调函数中调用普通成员函数。因为普通成员函数的调用必须是有对象为基础的,这里只有一个对象,所以只能调用那一个对象的普通成员函数,很显然这是不会满足条件的。

【解决方案2】:本方案解决多个类实例对象时方案1的问题。用映射表存所有对象地址,每个对象保存自己的ID号。

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(); //回调函数中调用非静态函数 
  }
}

总结:

回调函数是基于C编程的Windows SDK的技术,不是针对C++的,程序员可以将一个C函数直接作为回调函数,但是如果试图直接使用C++的成员函数作为回调函数将发生错误,甚至编译就不能通过。

普通的C++成员函数都隐含了一个传递函数作为参数,亦即“this”指针,C++通过传递一个指向自身的指针给其成员函数从而实现程序函数可以访问C++的数据成员。这也可以理解为什么C++类的多个实例可以共享成员函数但是确有不同的数据成员。由于this指针的作用,使得将一个CALLBACK型的成员函数作为回调函数安装时就会因为隐含的this指针使得函数参数个数不匹配,从而导致回调函数安装失败。
原文链接: https://www.cnblogs.com/stemon/p/4716468.html

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/220364

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年2月13日 上午10:53
下一篇 2023年2月13日 上午10:53

相关推荐