摘要: 介紹了事件委託機制的需求,各類解決方案的演變,最終提出模板化的事件委託機制,並給出較詳細的進化過程和原理說明。
關鍵詞: C++,委託,委託器,事件器,模板
第一章 基礎版實現
在平時的工做中,咱們常常會遇到如下狀況
void Do(int event_id)
{
…
}
void OnEvent(int event_id)
{
Do(event_id);
}
下面是成員函數版本
class A
{
public:
void Do(int event_id)
{
…
}
};
class B
{
public:
void OnEvent(int event_id)
{
a.Do(event_id);
}
private:
A a;
};
(這裏a或者是B的成員,或者是全局變量,或者經過OnEvent函數傳遞進來)
以上是通常狀況,當B的OnEvent還須要調用另外的函數或者其餘對象的函數時,就不得不對OnEvent函數做出改動,固然若是A的類型改變了,也要作相應改動,變成
void OnEvent(int event_id)
{
c.Run(event_id);
}
或者
void OnEvent(int event_id)
{
a.Do(event_id);
c.Run(event_id);
…
}
因爲需求的多變性,致使OnEvent函數面對不一樣的狀況有不一樣的實現,類B的複用性大大下降。咱們知道GUI是接收事件並做出處理的一個典型例子,若是按照以上方法,則每一種控件都須要被繼承,重載OnEvent函數,用以對應不一樣的事件響應,是一件很可怕的任務,
:(
第二章 多態版實現
2.1 單任務的實現
C++提供了多態機制,咱們可使用類的虛函數改善以上的問題。
(在C中可使用函數指針的方法,其本質是相同的,這個就由讀者本身發揮了)
class EventCallerBase
{
public:
// 基類使用純虛函數,派生類必須實現
virtual void Do(int event_id) = 0;
};
class Receiver
{
public:
void SetEventCaller(EventCallerBase* pCaller) { m_pCaller = pCaller; }
void OnEvent(int event_id)
{
if (m_pCaller)
m_pCaller->Do(event_id);
}
private:
EventCallerBase* m_pCaller;
};
class EventCallerA : public EventCallerBase
{
public:
virtual void Do(int event_id)
{
printf("EventCallerA do event %d.\r\n", event_id);
}
};
void main()
{
EventCallerA caller;
Receiver receiver;
receiver.SetEventCaller(&caller);
…
receiver.OnEvent(99);
}
輸出:EventCallerA do event 99.
2.2 多任務的實現
對於須要對多個對象調用其函數的狀況,用如下方式
EventCallerBase,EventCallerA的實現同上
class EventCallerB : public EventCallerBase
{
public:
virtual void Do(int event_id)
{
printf("EventCallerB do event %d.\r\n", event_id);
}
};
class Receiver
{
public:
void AddEventCaller(EventCallerBase* pCaller)
{
if (pCaller)
m_CallerList.push_back(pCaller);
}
void OnEvent(int event_id)
{
list<EventCallerBase*>::iterator it = m_CallerList.begin();
while (it != m_CallerList.end())
{
EventCallerBase* pCaller = *it;
if (pCaller)
pCaller->Do(event_id);
++it;
}
}
private:
list<EventCallerBase*> m_CallerList;
};
void main()
{
EventCallerA callerA;
EventCallerB callerB;
Receiver receiver;
receiver.AddEventCaller(&callerA);
receiver.AddEventCaller(&callerB);
…
receiver.OnEvent(99);
}
輸出:EventCallerA do event 99.
EventCallerB do event 99.
在以上方法中,類Receiver基本作到了重用,除了OnEvent參數類型和個數的改變,通常狀況下,當有事件發生,調用不一樣的事件處理函數時,只要繼承EventCallerBase類,實現Do函數,並在初始階段設定AddEventCaller便可。這種方法在GUI中,已經能儘量地重用發生事件部分的類和代碼,把主要工做放在實現事件響應的處理上。
2.3 對已有類的改造
這裏有個問題,若是有一個需求,好比窗口最大化,須要調用成員函數System::Maximize(),怎麼辦?類System是一個既有類,不能隨便改動,來繼承EventCallerBase。上面的方法豈不是不實用?
小小地動動腦筋,方法是有的:
class System
{
public:
void Maximize(void) { printf("Window is maximized.\r\n"); }
};
class EventCallerSystem : public EventCallerBase
{
public:
EventCallerSystem(System* pSystem) { m_pSystem = pSystem; }
virtual void Do(int event_id)
{
if (m_pSystem)
m_pSystem->Maximize()
}
private:
System* m_pSystem;
};
void main()
{
System system;
EventCallerSystem callerSystem(&system);
Receiver receiver;
receiver.AddEventCaller(&callerSystem);
…
receiver.OnEvent(99);
}
輸出:Window is maximized.
解決了問題,還留了一個小尾巴,就是要多實現一個EventCallerSystem類。
有沒有辦法把這個小尾巴也一併解決掉呢,這就到了這篇文章的主題――C++中的事件委託機制,此次咱們用到了C++的另外一個特性---模板。
第三章 事件委託版實現
3.1 函數指針的使用
咱們首先複習一下函數指針和成員函數指針。
3.1.1 函數指針
在C和C++語言中,一個命名爲my_func_ptr的函數指針指向以一個int和一個char*爲參數的函數,這個函數返回一個浮點值,聲明以下:
float (*my_func_ptr)(int, char *);
爲了便於理解,通常咱們使用typedef關鍵字。
typedef float (*MyFuncPtrType)(int, char *);
若是你的函數指針指向一個型如float some_func(int, char *)的函數,這樣作就能夠了:
MyFuncPtrType my_func_ptr = some_func;
當你想調用它所指向的函數時,能夠這樣寫:
(*my_func_ptr)(7, "HelloWorld");
或者
my_func_ptr(7, "HelloWorld");
3.1.2 成員函數指針
在C++程序中,不少函數是成員函數,即這些函數是某個類中的一部分。你不能夠像一個普通的函數指針那樣指向一個成員函數,正確的作法應該是,你必須使用一個成員函數指針。一個成員函數的指針指向類中的一個成員函數,並有相同的參數,聲明以下:
float (SomeClass::*my_memfunc_ptr)(int, char *);
將函數指針指向型如float SomeClass::some_member_func(int, char *)的函數,能夠這樣寫:
my_memfunc_ptr = &SomeClass::some_member_func;
當你想調用它所指向的成員函數時,能夠這樣寫:
SomeClass* x = new SomeClass;
(x->*my_memfunc_ptr)(6, "HelloWorld");
3.2 函數指針的大小
class A
{
public:
int Afunc() { return 2; };
};
class B
{
public:
int Bfunc() { return 3; };
};
class D: public A, public B
{
public:
int Dfunc() { return 5; };
};
int main()
{
printf("%d\n", sizeof(&main));
printf("%d\n", sizeof(&A::Afunc));
printf("%d\n", sizeof(&B::Bfunc));
printf("%d\n", sizeof(&D::Dfunc));
return 0;
}
輸出:
4
4
4
8
能夠看出,普通函數的指針大小是4,
普通類的成員函數的指針大小也是4,
對於多重繼承的類,成員函數的指針大小是8,
還有成員函數指針大小是12和16的狀況,在這裏就不展開了。
(須要特別注意的是,相同的代碼,在不一樣的編譯器下,函數指針的大小也不相同)。
對函數指針和成員函數指針的複習就到這裏。
3.3 C++中的事件委託
如下登場的是本文的主角:模板化實現的C++中的事件委託
3.3.1代碼
/////////////////////////////////////////////////////////////////////////////////
/// \class FuncCache
/// \brief 函數對象寄存器
/////////////////////////////////////////////////////////////////////////////////
template <typename ReturnType>
class FuncCache
{
static const int SIZE = 48;
typedef ReturnType (*func_caller)(FuncCache*);
/// \class MemberFuncAssist
/// \brief 對象成員函數寄存器的輔助器
class FuncCacheAssist
{
public:
/// \brief 構造函數,初始化。
FuncCacheAssist(FuncCache* pFunc)
{
m_Size = 0;
m_pFunc = pFunc;
// 讀取用偏移必須歸位
m_pFunc->m_Cur = 0;
}
/// \brief 析構函數。
~FuncCacheAssist(void)
{
// 彈出之前壓入的參數