学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老师了。
所以不能简单地使用类成员函数。一定要的话弄个静态的吧。
或者可以写个帮助类。等等
嗯,还有其他的毛病哦,来喷吧~