天天看點

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

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

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

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