zoukankan      html  css  js  c++  java
  • 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老师了。

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

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

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

     
  • 相关阅读:
    Sql Server Report 导出到EXCEL 指定行高
    SQLSqlserver中如何将一列数据,不重复的拼接成一个字符串
    SQL Server Management Studio无法记住密码
    nuget.org无法解析的办法
    js获取select标签选中的值及文本
    Word 如何实现表格快速一分为二
    sql server rdl report 如何用动态sql
    浏览器窗口刷新
    SWFUpload 在ie9上出现的bug
    历数PC发展史上的祖先们
  • 原文地址:https://www.cnblogs.com/pasoraku/p/2971808.html
Copyright © 2011-2022 走看看