回調函數必須要用static的原因

在之前的一篇回調函數簡單例子中就寫了一個簡單的回調小例子,這裏補充一下。

一個對象的this指針並不是對象本身的一部分,不會影響sizeof(對象)的結果。this作用域是在類內部,當在類的非靜態成員函數中訪問類的非靜態成員的時候,編譯器會自動將對象本身的地址作爲一個隱含參數傳遞給函數。也就是說,即使你沒有寫上this指針,編譯器在編譯的時候也是加上this的,它作爲非靜態成員函數的隱含形參,對各成員的訪問均通過this進行。

爲了實現回調,我們必須把this指針給轉換掉!可爲了在該函數中可以直接操作該類中的成員,我們必須保留this指針!所以這是矛盾的。

在類封裝回調函數:

 a.回調函數只能是全局的或是靜態的。
 b.全局函數會破壞類的封裝性,故不予採用。
 c.靜態函數只能訪問類的靜態成員,不能訪問類中非靜態成員

讓靜態函數訪問類的非靜態成員的方法:

在消息回調的函數參數中傳遞一個該類的指針即可,就像類中創建一個多線程的回調一樣.將類的指針傳遞給該回調函數,然後用該指針調用類的非靜態成員函數和指針.或者用一個類的全局指針數組,保存每一個創建出來的類的this指針,用全局指針去調用。如下:

 class A()

回調函數中訪問非靜態成員

  由於回調函數往往有固定定義,並不接受  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;  //這句話必須要在cpp一開始就寫,不然編譯會報無法解析的外部符號的錯。
  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(); //回調函數中調用非靜態函數 
      };
  }
 

原文連接:https://blog.csdn.net/u012072012/article/details/45537387

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章