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老師了。

所以不能簡單地使用類成員函數。一定要的話弄個靜態的吧。

或者可以寫個幫助類。等等

嗯,還有其他的毛病哦,來噴吧~

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM