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