學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老師了。
是以不能簡單地使用類成員函數。一定要的話弄個靜态的吧。
或者可以寫個幫助類。等等
嗯,還有其他的毛病哦,來噴吧~