欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

C++使用標準庫實現(xiàn)事件和委托以及信號和槽機制

 更新時間:2022年11月09日 11:30:52   作者:HW140701  
這篇文章主要為大家詳細介紹了C++如何使用標準庫實現(xiàn)事件和委托以及信號和槽機制,文中的示例代碼講解詳細,具有一定的借鑒價值,需要的可以參考一下

在日常的程序開發(fā)中我們經(jīng)常會遇到以下的實際問題:

  • 比如在一個文件下載完成時,發(fā)送郵件或者微信通知告知用戶;
  • 比如點擊一個按鈕時,執(zhí)行相應(yīng)的業(yè)務(wù)邏輯;
  • 比如當用戶的金額少于一個閾值時,通知用戶及時充值;

等等。

這些業(yè)務(wù)需求其實都對應(yīng)著觀察者模式,當一個對象的狀態(tài)發(fā)生改變或者達到某種條件,所有的觀察者對象都會得到通知,觀察者模式通過面向?qū)ο笤O(shè)計,實現(xiàn)軟件結(jié)構(gòu)的松耦合設(shè)計。

C#中的委托和事件以及Qt的信號和槽機制都是遵循了此種設(shè)計模式。在使用C#和Qt的過程中常常感嘆為什么C++標準庫不自帶這種快速開發(fā)的原生類呢(雖然boost中有),那么本文我們就使用C++模板實現(xiàn)一個簡單但是夠用的C++事件工具類。

1 .Net的委托和事件

我們首先看下C#中的委托示例

class Program
{
    //1、聲明委托類型
    public delegate void AddDelegate(int a, int b);
    
    //2、委托函數(shù)(方法),參數(shù)需要和委托參數(shù)一致
    public static void Add(int a, int b)
    {
        Console.WriteLine(a + b);
    }
    
    static void Main(string[] args)
    {
        //3、創(chuàng)建委托實例,將方法名Add作為參數(shù)綁定到該委托實例,也可以不使用new,直接AddDelegate addDelegate = Add;
        AddDelegate addDelegate = new AddDelegate(Add);
        //4、調(diào)用委托實例
        addDelegate(1, 2);
        Console.ReadKey();
    }
}

從上述代碼可以看出C#的委托是不是與C++的函數(shù)指針聲明很像,先聲明一種表明返回值和形參的函數(shù)形式,然后把一個符合這種形式的函數(shù)當做參數(shù)進行傳遞,并最后進行調(diào)用,類似于C的函數(shù)指針聲明以及C++的std::function。

看完委托之后,我們來看一個事件的示例,

public class Account
{
	private float bank_savings = 1000; // 存款金額
	public event Action OnInsufficientBalance; // 余額不足事件
	
	public void cosume(float money)
	{
		bank_savings -= money;
		if (bank_savings < 100)
		{
			OnInsufficientBalance.InVoke();
		}
	}
}

public class Notify
{
	public static void Email()
    {
		Console.WriteLine("Insufficient Balance");
    }
}



class Program
{
	static void Main(string[] args)
	{
		var account = new Account();
		account.OnInsufficientBalance += Notify.Email;
		
		account.cosume(1000);
	}
}

在上述代碼中我們聲明一個OnInsufficientBalance事件,這個事件在用戶賬戶低于100的時候觸發(fā),觸發(fā)函數(shù)使用郵件告知用戶。

2.Qt的信號和槽

Qt的信號和槽機制是由Qt實現(xiàn)的觀察者機制,可以通過信號觸發(fā)綁定的槽方法。

信號(Signal)就是在特定情況下被發(fā)射的事件,例如 PushButton 最常見的信號就是鼠標單擊時發(fā)射的 clicked() 信號。

槽(Slot)就是對信號響應(yīng)的函數(shù)。槽函數(shù)可以與一個信號關(guān)聯(lián),當信號被發(fā)射時,關(guān)聯(lián)的槽函數(shù)被自動執(zhí)行。

當點擊一個按鈕時,Qt發(fā)出按鈕被點擊的信號,然后觸發(fā)信號綁定的開發(fā)者的自定義槽方法。

Qt的信號和槽方法與.Net的委托和事件大致相同,其中信號對應(yīng)事件,槽函數(shù)對應(yīng)委托。

示例代碼如下:

button1 = new QPushButton("close",this);//創(chuàng)建按鈕,指定父對象
button2 = new QPushButton("print",this);//創(chuàng)建按鈕,指定父對象

connect(button1,&QPushButton::clicked,this,&QWidget::close);
connect(button2,&QPushButton::clicked,this,[](){
        qDebug() << "關(guān)閉成功";//打印關(guān)閉成功
    });

3.Duilib中委托和事件

在Duilib也有對委托和事件的簡單實現(xiàn),我們可以在UIDelegate.h和UIDelegate.cpp中看到相應(yīng)的實現(xiàn)。

UIDelegate.h

#ifndef __UIDELEGATE_H__
#define __UIDELEGATE_H__

#pragma once

namespace DuiLib {

class DUILIB_API CDelegateBase	 
{
public:
    CDelegateBase(void* pObject, void* pFn);
    CDelegateBase(const CDelegateBase& rhs);
    virtual ~CDelegateBase();
    bool Equals(const CDelegateBase& rhs) const;
    bool operator() (void* param);
    virtual CDelegateBase* Copy() const = 0; // add const for gcc

protected:
    void* GetFn();
    void* GetObject();
    virtual bool Invoke(void* param) = 0;

private:
    void* m_pObject;
    void* m_pFn;
};

class CDelegateStatic: public CDelegateBase
{
    typedef bool (*Fn)(void*);
public:
    CDelegateStatic(Fn pFn) : CDelegateBase(NULL, pFn) { } 
    CDelegateStatic(const CDelegateStatic& rhs) : CDelegateBase(rhs) { } 
    virtual CDelegateBase* Copy() const { return new CDelegateStatic(*this); }

protected:
    virtual bool Invoke(void* param)
    {
        Fn pFn = (Fn)GetFn();
        return (*pFn)(param); 
    }
};

template <class O, class T>
class CDelegate : public CDelegateBase
{
    typedef bool (T::* Fn)(void*);
public:
    CDelegate(O* pObj, Fn pFn) : CDelegateBase(pObj, *(void**)&pFn) { }
    CDelegate(const CDelegate& rhs) : CDelegateBase(rhs) { } 
    virtual CDelegateBase* Copy() const { return new CDelegate(*this); }

protected:
    virtual bool Invoke(void* param)
    {
		O* pObject = (O*) GetObject();
		union
		{
			void* ptr;
			Fn fn;
		} func = { GetFn() };
		return (pObject->*func.fn)(param);
    }  

private:
	Fn m_pFn;
};

template <class O, class T>
CDelegate<O, T> MakeDelegate(O* pObject, bool (T::* pFn)(void*))
{
    return CDelegate<O, T>(pObject, pFn);
}

inline CDelegateStatic MakeDelegate(bool (*pFn)(void*))
{
    return CDelegateStatic(pFn); 
}

class DUILIB_API CEventSource
{
    typedef bool (*FnType)(void*);
public:
    ~CEventSource();
    operator bool();
    void operator+= (const CDelegateBase& d); // add const for gcc
    void operator+= (FnType pFn);
    void operator-= (const CDelegateBase& d);
    void operator-= (FnType pFn);
    bool operator() (void* param);

protected:
    CDuiPtrArray m_aDelegates;
};

} // namespace DuiLib

#endif // __UIDELEGATE_H__

UIDelegate.cpp

#include "StdAfx.h"

namespace DuiLib {

CDelegateBase::CDelegateBase(void* pObject, void* pFn) 
{
    m_pObject = pObject;
    m_pFn = pFn; 
}

CDelegateBase::CDelegateBase(const CDelegateBase& rhs) 
{
    m_pObject = rhs.m_pObject;
    m_pFn = rhs.m_pFn; 
}

CDelegateBase::~CDelegateBase()
{

}

bool CDelegateBase::Equals(const CDelegateBase& rhs) const 
{
    return m_pObject == rhs.m_pObject && m_pFn == rhs.m_pFn; 
}

bool CDelegateBase::operator() (void* param) 
{
    return Invoke(param); 
}

void* CDelegateBase::GetFn() 
{
    return m_pFn; 
}

void* CDelegateBase::GetObject() 
{
    return m_pObject; 
}

CEventSource::~CEventSource()
{
    for( int i = 0; i < m_aDelegates.GetSize(); i++ ) {
        CDelegateBase* pObject = static_cast<CDelegateBase*>(m_aDelegates[i]);
        if( pObject) delete pObject;
    }
}

CEventSource::operator bool()
{
    return m_aDelegates.GetSize() > 0;
}

void CEventSource::operator+= (const CDelegateBase& d)
{ 
    for( int i = 0; i < m_aDelegates.GetSize(); i++ ) {
        CDelegateBase* pObject = static_cast<CDelegateBase*>(m_aDelegates[i]);
        if( pObject && pObject->Equals(d) ) return;
    }

    m_aDelegates.Add(d.Copy());
}

void CEventSource::operator+= (FnType pFn)
{ 
    (*this) += MakeDelegate(pFn);
}

void CEventSource::operator-= (const CDelegateBase& d) 
{
    for( int i = 0; i < m_aDelegates.GetSize(); i++ ) {
        CDelegateBase* pObject = static_cast<CDelegateBase*>(m_aDelegates[i]);
        if( pObject && pObject->Equals(d) ) {
            delete pObject;
            m_aDelegates.Remove(i);
            return;
        }
    }
}
void CEventSource::operator-= (FnType pFn)
{ 
    (*this) -= MakeDelegate(pFn);
}

bool CEventSource::operator() (void* param) 
{
    for( int i = 0; i < m_aDelegates.GetSize(); i++ ) {
        CDelegateBase* pObject = static_cast<CDelegateBase*>(m_aDelegates[i]);
        if( pObject && !(*pObject)(param) ) return false;
    }
    return true;
}

} // namespace DuiLib

從上述Duilib實現(xiàn)委托與事件機制的源碼,我們可以看出整個的實現(xiàn)思路,通過CEventSource創(chuàng)建事件,通過MakeDelegate函數(shù)構(gòu)建綁定到事件上的委托函數(shù)CDelegate<O, T>,而這種委托函數(shù)的形式只能是void(void*)的形式。然后通過CEventSource重載操作符+=和-=添加和刪除委托函數(shù)。Duilib這種方式應(yīng)該就是最簡單的事件和委托的原型,但是缺點是事件只能綁定固定形式的委托函數(shù)。

4.使用C++標準庫簡單實現(xiàn)事件觸發(fā)機制

第3節(jié)Duilib的委托和事件不能自定義事件所綁定委托函數(shù)的形式,在本節(jié)中我們使用C++標準庫對事件機制進行實現(xiàn),可以自定義事件綁定函數(shù)的形式。

具體的代碼如下:

Event.hpp

#ifndef _EVENT_H_
#define _EVENT_H_

#include <vector>
#include <functional>
#include <type_traits>
#include <memory>
#include <assert.h>


namespace stubbornhuang
{
	// 原型
	template<typename Prototype> class Event;


	// 特例
	template<typename ReturnType, typename ...Args>
	class Event <ReturnType(Args...)>
	{
	private:
		using return_type = ReturnType;
		using function_type = ReturnType(Args...);
		using stl_function_type = std::function<function_type>;
		using pointer = ReturnType(*)(Args...);

	private:
		class EventHandler
		{
		public:
			EventHandler(stl_function_type func)
			{
				assert(func != nullptr);
				m_Handler = func;
			}

			void Invoke(Args ...args)
			{
				if (m_Handler != nullptr)
				{
					m_Handler(args...);
				}
			}

		private:
			stl_function_type m_Handler;
		};

	public:
		void operator += (stl_function_type func)
		{
			std::shared_ptr<EventHandler> pEventHandler = std::make_shared<EventHandler>(func);

			if (pEventHandler != nullptr)
			{
				m_HandlerVector.push_back(std::move(pEventHandler));
			}
		}

		void Connect(stl_function_type func)
		{
			std::shared_ptr<EventHandler> pEventHandler = std::make_shared<EventHandler>(func);

			if (pEventHandler != nullptr)
			{
				m_HandlerVector.push_back(std::move(pEventHandler));
			}
		}

		void operator() (Args ...args)
		{
			for (int i = 0; i < m_HandlerVector.size(); ++i)
			{
				if (m_HandlerVector[i] != nullptr)
				{
					m_HandlerVector[i]->Invoke(args...);
				}
			}
		}

		void Trigger(Args ...args)
		{
			for (int i = 0; i < m_HandlerVector.size(); ++i)
			{
				if (m_HandlerVector[i] != nullptr)
				{
					m_HandlerVector[i]->Invoke(args...);
				}
			}
		}

	private:
		std::vector<std::shared_ptr<EventHandler>> m_HandlerVector;
	};
}


#endif // !_EVENT_H_

在上述代碼中我們使用template<typename ReturnType, typename ...Args>對事件類Event進行了模板化,使用變參模板typename ...Args自定義事件綁定的委托函數(shù)參數(shù)列表,可以接受多個不同類型的參數(shù)。使用std::vector存儲綁定事件的std::function<ReturnType(Args...)>的委托函數(shù),并重載+=操作符添加委托函數(shù)。

上述事件工具類Event的使用示例如下:

#include <iostream>

#include "Event.h"

class Button
{
public:
	Button()
	{

	}

	virtual~Button()
	{

	}

public:
	stubbornhuang::Event<void()> OnClick;
};

void Click()
{
	std::cout << "Button Click" << std::endl;
}


class Example
{
public:
	void Click()
	{
		std::cout << "Example Click" << std::endl;
	}
};

int main()
{
	Button button;

	button.OnClick += Click; // 靜態(tài)函數(shù)做委托函數(shù)

	Example example;
	button.OnClick += std::bind(&Example::Click, example); // 成員函數(shù)做委托函數(shù) 

	button.OnClick += []() { std::cout << "Lambda Click" << std::endl;  }; // 匿名函數(shù)做委托函數(shù)

	button.OnClick();

	return 0;
}

執(zhí)行結(jié)果:

Button Click
Example Click
Lambda Click

由于std::function的超強特性,我們可以為事件綁定靜態(tài)函數(shù)、類成員函數(shù)以及匿名函數(shù)。

5.總結(jié)

在本文中,我們對.Net的事件和委托,Qt的信號和槽進行了簡單的介紹,然后通過引入Duilib中對于事件和委托的簡單實現(xiàn),進而擴展了自定義的簡單事件類Event,此類實現(xiàn)的比較簡單,但是包含了事件實踐的核心思想,自己對于模板類,以及變參模板的使用又有了新的體會。

到此這篇關(guān)于C++使用標準庫實現(xiàn)事件和委托以及信號和槽機制的文章就介紹到這了,更多相關(guān)C++標準庫內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • C++??系統(tǒng)IO流介紹

    C++??系統(tǒng)IO流介紹

    這篇文章主要介紹了C++系統(tǒng)IO流,大部分人都是從輸出"Hello?World"開始的,本文會介紹C++中的IO細節(jié),需要的朋友可以參考一下,希望對大家有所幫助
    2021-12-12
  • c++實現(xiàn)獲取當前時間(精確至秒,毫秒和微妙)

    c++實現(xiàn)獲取當前時間(精確至秒,毫秒和微妙)

    這篇文章主要為大家詳細介紹了c++實現(xiàn)獲取當前時間(可以精確至秒,毫秒和微妙)的相關(guān)知識,文中的示例代碼講解詳細,感興趣的小伙伴可以參考一下
    2023-11-11
  • C語言 structural body結(jié)構(gòu)體詳解用法

    C語言 structural body結(jié)構(gòu)體詳解用法

    C 數(shù)組允許定義可存儲相同類型數(shù)據(jù)項的變量,結(jié)構(gòu)是 C 編程中另一種用戶自定義的可用的數(shù)據(jù)類型,它允許您存儲不同類型的數(shù)據(jù)項,結(jié)構(gòu)用于表示一條記錄,假設(shè)您想要跟蹤圖書館中書本的動態(tài),您可能需要跟蹤每本書的下列屬性
    2021-10-10
  • C語言指針必備基礎(chǔ)全面覆蓋

    C語言指針必備基礎(chǔ)全面覆蓋

    數(shù)據(jù)對象是指存儲在內(nèi)存中的一個指定數(shù)據(jù)類型的數(shù)值或字符串,它們都有一個自己的地址,指針是保存這個地址的變量,本篇文章帶你掌握C語言指針的用法
    2021-10-10
  • C語言初學(xué)者代碼中的常見錯誤與問題

    C語言初學(xué)者代碼中的常見錯誤與問題

    C語言初學(xué)者犯過的很多錯誤都非常典型,在初學(xué)者中非常普遍,于是整理了一下,應(yīng)該對其他初學(xué)者有借鑒意義
    2013-11-11
  • C++中的整型

    C++中的整型

    這篇文章我們來聊聊C++中的整型,整型即整數(shù),與小數(shù)對應(yīng)。許多語言只能表示一種整型(如Python),而在C++當中根據(jù)整數(shù)的范圍提供了好幾種不同的整型,下面文章我們就來看看具體是哪幾種,需要的朋友也可以參考一下
    2021-11-11
  • VS2019開發(fā)簡單的C/C++動態(tài)鏈接庫并進行調(diào)用的實現(xiàn)

    VS2019開發(fā)簡單的C/C++動態(tài)鏈接庫并進行調(diào)用的實現(xiàn)

    這篇文章主要介紹了VS2019開發(fā)簡單的C/C++動態(tài)鏈接庫并進行調(diào)用的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-03-03
  • C語言特殊符號的補充理解

    C語言特殊符號的補充理解

    這篇文章主要為大家介紹了C語言特殊符號的使用補充理解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-02-02
  • 探討C語言的那些小秘密之斷言

    探討C語言的那些小秘密之斷言

    我盡可能的把我所理解的斷言的使用講解清楚,希望我在此所講的斷言能夠?qū)δ阌兴鶐椭屇阋院竽軌蛟诖a中靈活使用斷言
    2013-09-09
  • C語言實現(xiàn)循環(huán)打印星號圖形再鏤空

    C語言實現(xiàn)循環(huán)打印星號圖形再鏤空

    這篇文章主要介紹了C語言實現(xiàn)循環(huán)打印星號圖形再鏤空,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-11-11

最新評論