學C#的時候用C#的事件很舒服,像我這樣低級的使用者,一個+=就省去了許多麻煩。
於是我想着C++中是怎么做呢?
不如山寨一下。
第一步,首先是委托
這個好像是C++中的函數指針,那么就這樣
typedef void (*CREventFunc)(void* sender, void* param);
模仿C#的事件,第一個參數是事件發生者的指針,第二個是事件參數。
但是呢,因為懶啊,具體類型沒有考慮去規定,暫且先void*用着吧。
第二步,定義山寨的Event類
因為C#的事件貌似是個鏈表一樣的存在。。。所以就在里面搞個鏈表似的東東吧。
然后是要重載+=和-=兩個操作符,這個是山寨事件用法的關鍵哦~
在觸發事件的地方也要准備個方法,可惜已經變成類了,直接括號不能用,就弄個函數叫SetEvent好了,額。。。
最后這個類聲明大概是這樣:
class CREvent { private: struct CREventNode { CREventFunc Func; CREventNode* pNext; }; CREventNode* m_pHead; CREventNode* m_pTail; public: CREvent(void); ~CREvent(void); CREvent& operator += (const CREventFunc& right); CREvent& operator -= (const CREventFunc& right); void SetEvent(void* sender, void* param); };
接着來實現這些方法吧
首先是構造函數和析構函數
CREvent::CREvent(void):m_pHead(NULL),m_pTail(NULL) { } CREvent::~CREvent(void) { CREventNode* pNode = NULL; while(m_pHead != NULL){ pNode = m_pHead->pNext;//這里曾經犯過一個錯誤 delete m_pHead; m_pHead = pNode; } //此時m_pHead已經為NULL }
然后是+=和-=的實現。。。因為懶啊,所以右參數直接讓函數來做了,本來應該是要另外弄個EventHandler似的類的。
在執行這兩個操作符的時候對內部維護的鏈表進行增刪操作。
CREvent& CREvent::operator += (const CREventFunc& right) { CREventNode* pNode = new CREventNode(); pNode->Func = right; pNode->pNext = NULL; if(m_pHead == NULL){ m_pHead = pNode; m_pTail = m_pHead; }else{ m_pTail->pNext = pNode; m_pTail = pNode; } return (*this); } CREvent& CREvent::operator -= (const CREventFunc& right) { //遍歷鏈表 CREventNode* pNode = m_pHead; CREventNode* pPreNode = NULL; while(pNode!=NULL){ if(pNode->Func == right){ //刪除這個節點 if(pPreNode == NULL){ m_pHead = pNode->pNext; }else{ pPreNode->pNext = pNode->pNext; } delete pNode; break;//一次只刪一個哦親 } pPreNode = pNode; //保存前一個節點 pNode = pNode->pNext; //設置為下一個節點 } return (*this); }
最后就是事件的觸發調用的方法了。
void CREvent::SetEvent(void* sender, void* param) { CREventNode* pNode = m_pHead; while(pNode != NULL){ pNode->Func(sender, param); pNode = pNode->pNext; } }
以上,Event類封裝基本完畢。
第三步,測試
測試。。。就用熱水器好了。
class WaterHeater { public : WaterHeater():m_nTemperature(0){} ~WaterHeater(){} private: int m_nTemperature; public: void Heating(); CREvent WarningEvent; };
一個溫度成員和一個加熱方法,還有就是山寨的Event。設計在水加熱到90度的時候發出警報(就是觸發Warning事件)。
加熱方法的實現:
void WaterHeater::Heat() { while(m_nTemperature<100){ m_nTemperature++; //test output這里為了方法測試加入了iostream頭,可以輸出溫度 std::cout<<m_nTemperature<<std::endl; //同樣為了模擬長時間加入windows.h頭文件 Sleep(100); if(m_nTemperature >90){ WarningEvent.SetEvent((void*)this, (void*)0);//你懂的 } } }
好了,這樣熱水器就算是馬馬虎虎設計好了。
完工我們的main函數吧~
#include <iostream> using namespace std; #include "WaterHeater.h" void w1(void* sender, void* param) { cout<<"warning111"<<endl; } void w2(void* sender, void* param) { cout<<"warning222"<<endl; } void w3(void* sender, void* param) { cout<<"warning333"<<endl; } void w4(void* sender, void* param) { cout<<"warning444"<<endl; } void w5(void* sender, void* param) { cout<<"warning555"<<endl; } int main() { WaterHeater heater; heater.WarningEvent += w1; heater.WarningEvent += w2; heater.WarningEvent += w2; heater.WarningEvent += w3; heater.WarningEvent += w4; heater.WarningEvent += w4; heater.WarningEvent += w2; heater.WarningEvent += w4; heater.WarningEvent += w5; heater.WarningEvent -= w4; heater.WarningEvent -= w4; heater.Heating(); system("pause"); return 0; }
F5運行一下看看吧~
結尾,那些被懶漢逃避的問題
肯定有人注意到這里,使用的都是全局函數。。。嘿嘿。
貌似是說全局函數的函數指針是四個字節,而類成員函數的函數指針似乎包含了類信息所以大小不一樣。具體的又得要請教google老師了。
所以不能簡單地使用類成員函數。一定要的話弄個靜態的吧。
或者可以寫個幫助類。等等
嗯,還有其他的毛病哦,來噴吧~