C++山寨CSharp事件

学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老师了。

所以不能简单地使用类成员函数。一定要的话弄个静态的吧。

或者可以写个帮助类。等等

嗯,还有其他的毛病哦,来喷吧~

 
上一篇:mono3.2和monodevelop4.0在ubuntu12.04上两天的苦战


下一篇:最近做的一个store app音乐箱