c++ 實現delegate(支持普通函數與成員函數)

最近開始寫了一些c#的東西,有個delegate的東西蠻有意思。

比如如下C#代碼
 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CSharpConsole
{
    public delegate int Test(string str);
    public class DelegaeFunc
    {
        public static int func1(string str)
        {
            Console.WriteLine("Func1" + str);
            return 1;
        }
        public static int func2(string str)
        {
            Console.WriteLine("Func2" + str);
            return 2;
        }
    }  
    class Program
    {

        public static int func3(string str)
        {
            Console.WriteLine("Func3" + str);
            return 3;
        }
        static void Main(string[] args)
        {
            Test printHandler = new Test(func3);
            printHandler += DelegaeFunc.func2;
            printHandler += DelegaeFunc.func1;
            int res = printHandler("hanliangwei");
            Console.WriteLine(res);
            Console.ReadKey();

        }
    }
}

程序會依次執行func3,func2,func1,最終返回值是func1的返回值。有點兒類似於事件綁定的意思。不過單純的這種方式應該是不能綁定非靜態成員函數的,winform裏封裝得EventHandler似乎可以?初學剛用,記不太清了。下面給出c++的模擬實現。

c++模擬的版本可支持普通函數與成員函數,主要是用模板來指定返回值與類。對於函數參數定爲只有一個void *類型。因爲對於多個自定義的參數,我除了多定義幾個類,其他好像也沒有想到一個好的辦法。而且想着win32sdk中不是很多都是這麼做的嘛。void*其實可以做很多事兒了。忽然想到應該用兩個void*的,這樣可能更靈活一點?

具體實現:
對於普通函數,其實很簡單,只要定義好函數指針,再定義一個list,保存下來每個函數指針即可,而對於類,需要保存它的成員函數地址以及調用它的那個對象,由於是同一個類中,所以對於Delegate的類模板只能有一個模板參數,就是返回值,那對於成員函數類型的就應該在添加函數上做一個模板函數來萃取出這個類對象的類型,那這時候list該怎麼定義?所以需要一個類來輔助,輔助存儲類對象與成員函數指針,所以這個輔助類應該有兩個模板參數,class_type 與 return_type,但是有一個問題,在存儲於Delegate類中的這個list沒法給出class_type怎麼辦,所以還需要給輔助類加一個基類,這樣在list中就存儲基類的指針,添加的時候添加的是派生類的對象,利用多態進行執行代理的成員函數。

(此方法借鑑了一點一個叫sigslot的庫,是c++寫的一個模仿qt的信號槽庫)

所以代碼如下

#include <iostream>
#include<list>
using namespace std;
template<typename Return_Type>
class ClassProxyBase
{
public:
	virtual Return_Type Execute(void * p) { return 0; }
};
template<typename ClassType, typename Return_Type>
class ClassProxy:public ClassProxyBase<Return_Type>
{
public:
	typedef Return_Type(ClassType::*PMemFunc)(void *p);
	ClassProxy(ClassType* pObj, PMemFunc memFunc) :m_pObj(pObj), m_lpfnMemfunc(memFunc) {}
	
	Return_Type Execute(void * p)
	{
		return (m_pObj->*m_lpfnMemfunc)(p);
	}
private:
	ClassType *m_pObj;
	PMemFunc m_lpfnMemfunc;
};
template<typename Return_Type>
class Delegate
{
public:
	Delegate() {}
	~Delegate()
	{
		for (auto & lpfn : m_listMemberFuncPointers)
		{
			delete lpfn;
		}
	}
	typedef Return_Type(*PFUNC)(void *);
	Return_Type operator()(void * p)
	{
		Return_Type returnValue;
		for (auto & lpfn : m_listFunctionPointers)
		{
			returnValue = (*lpfn)(p);
		}
		for (auto & lpfn : m_listMemberFuncPointers)
		{
			returnValue = lpfn->Execute(p);
		}
		return returnValue;
	}
	Return_Type operator =(PFUNC ptr)
	{
		if (ptr == NULL)
			return;
		m_listFunctionPointers.clear();
		m_listFunctionPointers.push_back(ptr);
	}
	void operator += (PFUNC ptr)
	{
		m_listFunctionPointers.push_back(ptr);
	}
private:
	list<PFUNC> m_listFunctionPointers;
	//下面是爲了支持類的成員函數
public:
	template<class ClassType>
	void Add(ClassType * pObj, Return_Type(ClassType::*pMemfunc)(void *p))
	{
		ClassProxy<ClassType,Return_Type> *obj = new ClassProxy<ClassType, Return_Type>(pObj, pMemfunc);
		m_listMemberFuncPointers.push_back(obj);
	}
private:
	list<ClassProxyBase<Return_Type>*>m_listMemberFuncPointers;

};
class Test
{
public:
	int test(void * p)
	{ 
		cout << "class Test:" << *(int*)p << endl;
		return 30;
	}
};
int func1(void *p) 
{
	int * ptr = (int *)p;
	cout << "func1:" << *(int*)p<<endl;
	*ptr = 20;
	return 1;
}
int func2(void *p)
{
	cout << "func2:" << *(int*)p<<endl;
	return 2;
}
int main()
{	
	Delegate<int> delegateObj;
	delegateObj += func1;
	delegateObj += func2;

	Test obj;
	delegateObj.Add(&obj,&Test::test);
	int t = 10;
	int res = delegateObj((void*)&t);
	cout << "res:" << res << endl;

	system("pause");
}

執行結果如下:
func1:10                      
func2:20                      
class Test:20
res:30
                                                                  

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章