C++实现委托机制(二)

1.引言:

             上一篇文章已经介绍了如何构建一个无参数无返回值的函数指针的委托,这篇文章将对上一文章所述委托进行扩展,使得可以注册任意函数指针,不过再讲篇内容之前先要介绍一下实现这个功能所需要了解的C++11的一个新特性———可变参数模板


2.可变参数模板

              template(模板)是源于将类型和实现(算法)分离开的思想,为了让写好的算法适用于更多类型使得出现了模板,模板使得参数类别任意化,如果再加上“参数个数的任意化”,那么在参数方面的设计手段就基本上齐备了,有了variadic template 显然可以让设计出来的函数或是类有更大的复用性。因为有很多处理都是与“处理对象的个数”关系不大的,比如说打屏(printf),比如说比较大小(max,min),比如函数绑定子(bind,function要对应各种可能的函数就要能“任意”参数个数和类型)。如果不能对应任意个参数,那么就总会有人无法重用已有的实现,而不得不再重复地写一个自己需要的处理,而共通库的实现者为了尽可能地让自己写的类(函数)能复用在更多的场景,也不得不重复地写很多的代码或是用诡异的技巧,宏之类的去实现有限个“任意参数”的对应。

             所以在C++11中引入了这个新语法,下面我们先看一个例子,了解基本的语法:

template<typename ... Params> class MyClass;
 
MyClass<int, string> a; 

  在上面这个例子我们可以看到,在定义和申明中用 typename...  表示一个参数包,这样我们使用的时候,传入一个<int,string>那么int,string就会被打包入Params中。恩,你可以吧这个参数包看做一个参数的结构体,并且这个结构体的大小是动态的,并且是匿名的。恩,也就是说你无法使用Params[0]这样的语法去随机调用某个类型。听到这里,觉得是匿名的那应该怎么使用呢?答案是递归展开!!!我们先来看一个函数的例子,假如我想写一个Max函数求传入所有参数的最大值,这个参数支持(int,double,float),返回值为double.那么函数原型你觉得大概可能是这样子的:

template<typename ...Params>
double Max(Params... _params)
{
	double Maxnum;

	//求解过程

	return Maxnum;
}

但是这样写你会发现你无法获取到任何传入参数的类型和值。恩,所以我们来看下如何使用递归来获得每个参数的类型和值。

template<typename Head,typename ...Tail>
double Max(Head first,Tail... rest)
{
	double Maxnum;
	Maxnum = Max(rest...);
	if (Maxnum < first)
		Maxnum = first;
	return Maxnum;
}

可以看到,我将函数参数分离了一个出来作为Head,这样我就可以每次处理一个变量,使用递归下降,每次递归减少一个参数。注意:上面的rest...表示参数展开,也就是说将这个参数包展开然后当做参数传进去。但是这样做还是不够的,大家都知道递归需要一个出口。所以说我们需要一个重载版本(因为模板函数不能偏特化)。

template<typename Head>
double Max(Head first)
{
	return first;
}
这样就有了一个递归出口了。好,我们结合以上的代码写成一个完整的程序,看下如何使用的:

#include<iostream>
using namespace std;

template<typename Head, typename ...Tail>
double Max(Head first, Tail... rest)
{
	double Maxnum=0;
	Maxnum = Max(rest...);
	if (Maxnum < first)
		Maxnum = first;
	return Maxnum;
}
template<typename Head>
double Max(Head first)
{
	return first;
}

int main()
{
	cout << Max(1, 3, 3.4, 5.1, 1.5);
}
输出结果是: 5.1,同样的,模板类是通过私有继承来实现递归的。

template<typename Head, typename... Tail>
class tuple<Head, Tail...> : private tuple<Tail...>{
	Head head;
public:
	/* implementation */
};
template<typename Head>
class tuple<Head> {
	/* one-tuple implementation */
};

  以上可变参数的基本内容就讲完了,如果还有不懂的可以自行百度

3.任意参数个数、返回值任意的函数指针委托:

     同样的,我们还是先对接口进行修改,因为函数指针再不是void(*)(void)所以,接口也需要是一个模板类,而且还需要是一个可变参数模板。
template<typename ReturnType, typename ...ParamType>
class IDelegate
{
public:
	IDelegate(){}
	virtual ~IDelegate(){}
	virtual bool isType(const std::type_info& _type) = 0;
	virtual ReturnType invoke(ParamType ... params) = 0;
	virtual bool compare(IDelegate<ReturnType, ParamType...> *_delegate) const = 0;
};
  这样,这个接口的invoke函数将会是根据你的函数类型来动态生成对应的模板了。
      同样的,我们把剩下三个类按照如此进行改造:

//StaticDelegate 普通函数的委托

template<typename ReturnType, typename ...ParamType>
class CStaticDelegate :
    public IDelegate<ReturnType, ParamType...>
{
public:

    typedef  ReturnType(*Func)(ParamType...);

    CStaticDelegate(Func _func) : mFunc(_func) { }

    virtual bool isType(const std::type_info& _type) { return typeid(CStaticDelegate<ReturnType, ParamType...>) == _type; }

    virtual ReturnType invoke(ParamType ... params) { return mFunc(params...); }

    virtual bool compare(IDelegate<ReturnType, ParamType ...> *_delegate)const
    {
        if (0 == _delegate || !_delegate->isType(typeid(CStaticDelegate<ReturnType, ParamType ...>))) return false;
        CStaticDelegate<ReturnType, ParamType ...> * cast = static_cast<CStaticDelegate<ReturnType, ParamType ...>*>(_delegate);
        return cast->mFunc == mFunc;
    }

    virtual ~CStaticDelegate(){}
private:
    Func mFunc;
};

//成员函数委托
template<typename T, typename ReturnType, typename ...ParamType>
class CMethodDelegate :
    public IDelegate<ReturnType, ParamType...>
{
public:
    typedef ReturnType(T::*Method)(ParamType...);

    CMethodDelegate(T * _object, Method _method) : mObject(_object), mMethod(_method) { }

    virtual bool isType(const std::type_info& _type) { return typeid(CMethodDelegate<T, ReturnType, ParamType...>) == _type; }

    virtual ReturnType invoke(ParamType...params)
    {
        (mObject->*mMethod)(params...);
    }

    virtual bool compare(IDelegate<ReturnType, ParamType...> *_delegate) const
    {
        if (0 == _delegate || !_delegate->isType(typeid(CMethodDelegate<ReturnType, ParamType...>))) return false;
        CMethodDelegate<ReturnType, ParamType...>* cast = static_cast<CMethodDelegate<ReturnType, ParamType...>*>(_delegate);
        return cast->mObject == mObject && cast->mMethod == mMethod;
    }

    CMethodDelegate(){}
    virtual ~CMethodDelegate(){}
private:
    T * mObject;
    Method mMethod;
};

//多播委托
template<typename ReturnType, typename ...ParamType>
class CMultiDelegate
{
    
public:
    
    typedef std::list<IDelegate<ReturnType, ParamType...>*> ListDelegate;
    typedef typename ListDelegate::iterator ListDelegateIterator;
    typedef typename ListDelegate::const_iterator ConstListDelegateIterator;

    CMultiDelegate() { }
    ~CMultiDelegate() { clear(); }

    bool empty() const
    {
        for (ConstListDelegateIterator iter = mListDelegates.begin(); iter != mListDelegates.end(); ++iter)
        {
            if (*iter) return false;
        }
        return true;
    }

    void clear()
    {
        for (ListDelegateIterator iter = mListDelegates.begin(); iter != mListDelegates.end(); ++iter)
        {
            if (*iter)
            {
                delete (*iter);
                (*iter) = nullptr;
            }
        }
    }


    CMultiDelegate<ReturnType, ParamType...>& operator+=(IDelegate<ReturnType, ParamType...>* _delegate)
    {
        for (ListDelegateIterator iter = mListDelegates.begin(); iter != mListDelegates.end(); ++iter)
        {
            if ((*iter) && (*iter)->compare(_delegate))
            {
                delete _delegate;
                return *this;
            }
        }
        mListDelegates.push_back(_delegate);
        return *this;
    }

    CMultiDelegate<ReturnType, ParamType...>& operator-=(IDelegate<ReturnType, ParamType...>* _delegate)
    {
        for (ListDelegateIterator iter = mListDelegates.begin(); iter != mListDelegates.end(); ++iter)
        {
            if ((*iter) && (*iter)->compare(_delegate))
            {
                if ((*iter) != _delegate) delete (*iter);       //避免同一个地址被delete两次
                (*iter) = 0;
                break;
            }
        }
        delete _delegate;
        return *this;
    }

    std::vector<ReturnType> operator()(ParamType... params)
    {
        ListDelegateIterator iter = mListDelegates.begin();
        std::vector<ReturnType> _Results;
        while (iter != mListDelegates.end())
        {
            if (0 == (*iter))
            {
                iter = mListDelegates.erase(iter);
            }
            else
            {
                _Results.push_back((*iter)->invoke(params...));
                ++iter;
            }
        }
        return _Results;
    }
private:
    CMultiDelegate<ReturnType, ParamType...>(const CMultiDelegate& _event);
    CMultiDelegate<ReturnType, ParamType...>& operator=(const CMultiDelegate& _event);

private:
    ListDelegate mListDelegates;
};

但是这样写你会发现你在newDelegate里面对于传来的函数指针进行new CStaticDelegate或者new CMethodDelegate的时候需要制定函数返回值、参数的个数和类型,这显然不满足动态类型演化。这时候我们想,能不能给定一个函数指针,让代码自动去识别这个函数的返回值和参数呢?答案是可以的,我们只需要对上面的CStaticDelegate和CMethodDelegate特化一个版本即可。
     这里使用了一个小技巧:通过函数指针去得到函数返回值、参数个数类型。详情可以看看这里:http://bbs.csdn.net/topics/390652170?page=1
     这里我就直接贴我写好的代码:

//普通函数的委托特化版本
template<typename ReturnType, typename ...ParamType>
class CStaticDelegate<ReturnType(*)(ParamType ...)> :
    public IDelegate<ReturnType, ParamType ...>
{
public:

    //定义 Func 为 void (void) 函数类型指针。
    typedef  ReturnType(*Func)(ParamType...);

    CStaticDelegate(Func _func) : mFunc(_func) { }

    virtual bool isType(const std::type_info& _type) { return typeid(CStaticDelegate<ReturnType(*)(ParamType ...)>) == _type; }

    virtual ReturnType invoke(ParamType ... params) { return mFunc(params...); }

    virtual bool compare(IDelegate<ReturnType, ParamType ...> *_delegate)const
    {
        if (0 == _delegate || !_delegate->isType(typeid(CStaticDelegate<ReturnType(*)(ParamType ...)>))) return false;
        CStaticDelegate<ReturnType(*)(ParamType ...)> * cast = static_cast<CStaticDelegate<ReturnType(*)(ParamType ...)>*>(_delegate);
        return cast->mFunc == mFunc;
    }

    virtual ~CStaticDelegate(){}
private:
    Func mFunc;
};

//成员函数委托特化
template<typename T, typename ReturnType, typename ...ParamType>
class CMethodDelegate<T,ReturnType (T:: *)(ParamType...)> :
    public IDelegate<ReturnType, ParamType...>
{
public:
    typedef ReturnType(T::*Method)(ParamType...);

    CMethodDelegate(T * _object, Method _method) : mObject(_object), mMethod(_method) { }

    virtual bool isType(const std::type_info& _type) { return typeid(CMethodDelegate<T,ReturnType(T:: *)(ParamType...)>) == _type; }

    virtual ReturnType invoke(ParamType...params)
    {
        return (mObject->*mMethod)(params...);
    }

    virtual bool compare(IDelegate<ReturnType, ParamType...> *_delegate) const
    {
        if (0 == _delegate || !_delegate->isType(typeid(CMethodDelegate<T, ReturnType(T:: *)(ParamType...)>))) return false;
        CMethodDelegate<T, ReturnType(T:: *)(ParamType...)>* cast = static_cast<CMethodDelegate<T, ReturnType(T:: *)(ParamType...)>*>(_delegate);
        return cast->mObject == mObject && cast->mMethod == mMethod;
    }

    CMethodDelegate(){}
    virtual ~CMethodDelegate(){}
private:
    T * mObject;
    Method mMethod;
};

  这样我生成的时候只需要new CStaticDelegate<decltype(Func)>(Func),而特化版本的可以自动识别这个Func的返回值、参数个数和类型。
     最后我给出newDelegate的代码:
template< typename T>
CStaticDelegate<T>* newDelegate(T func)
{
    return new CStaticDelegate<T>(func);
}
template< typename T,typename F>
CMethodDelegate<T,F>* newDelegate(T * _object, F func)
{
    return new CMethodDelegate<T, F>(_object, func);
}
  写到这里基本上可变参数的委托就完成了,不过还需要注意一点就是void无返回值类型多播委托需要特殊处理。所以我们还需要一个多播委托对于ReturnType为void这个情况的特化。

template< typename ...ParamType>
class CMultiDelegate<void, ParamType...>
{

public:

    typedef std::list<IDelegate<void, ParamType...>*> ListDelegate;
    typedef typename ListDelegate::iterator ListDelegateIterator;
    typedef typename ListDelegate::const_iterator ConstListDelegateIterator;

    CMultiDelegate() { }
    ~CMultiDelegate() { clear(); }

    bool empty() const
    {
        for (ConstListDelegateIterator iter = mListDelegates.begin(); iter != mListDelegates.end(); ++iter)
        {
            if (*iter) return false;
        }
        return true;
    }

    void clear()
    {
        for (ListDelegateIterator iter = mListDelegates.begin(); iter != mListDelegates.end(); ++iter)
        {
            if (*iter)
            {
                delete (*iter);
                (*iter) = nullptr;
            }
        }
    }

    CMultiDelegate<void, ParamType...>& operator+=(IDelegate<void, ParamType...>* _delegate)
    {
        for (ListDelegateIterator iter = mListDelegates.begin(); iter != mListDelegates.end(); ++iter)
        {
            if ((*iter) && (*iter)->compare(_delegate))
            {
                delete _delegate;
                return *this;
            }
        }
        mListDelegates.push_back(_delegate);
        return *this;
    }

    CMultiDelegate<void, ParamType...>& operator-=(IDelegate<void, ParamType...>* _delegate)
    {
        for (ListDelegateIterator iter = mListDelegates.begin(); iter != mListDelegates.end(); ++iter)
        {
            if ((*iter) && (*iter)->compare(_delegate))
            {
                if ((*iter) != _delegate) delete (*iter);       //避免同一个地址被delete两次
                (*iter) = 0;
                break;
            }
        }
        delete _delegate;
        return *this;
    }

    void operator()(ParamType... params)
    {
        ListDelegateIterator iter = mListDelegates.begin();
        while (iter != mListDelegates.end())
        {
            if (0 == (*iter))
            {
                iter = mListDelegates.erase(iter);
            }
            else
            {
                (*iter)->invoke(params...);
                ++iter;
            }
        }
    }
private:
    CMultiDelegate<void, ParamType...>(const CMultiDelegate& _event);
    CMultiDelegate<void, ParamType...>& operator=(const CMultiDelegate& _event);

private:
    ListDelegate mListDelegates;
};
      所有代码都已经贴出来了,我把这些模板全部放到了MyDelegate.h头文件中。下面给一个使用的例子:

#include "MyDelegate.h"  
using namespace Delegate;  
  
void NormalFunc(int a)  
{  
    printf("这里是普通函数 :%d\n", a);  
}  
  
class A  
{  
public:  
    static void StaticFunc(int a)  
    {  
        printf("这里是成员静态函数 : %d\n", a);  
    }  
    void MemberFunc(int a)  
    {  
        printf("这里是成员非静态函数 : %d\n", a);  
    }  
};  
int _tmain(int argc, _TCHAR* argv[])  
{  
    //首先创建了一个返回值为 void ,参数为int 的一个委托。  
    CMultiDelegate<void, int> e;  
  
    //将三个函数注册到该委托中  
    e += newDelegate(NormalFunc);  
    e += newDelegate(A::StaticFunc);  
    e += newDelegate(&A(), &A::MemberFunc);  
  
    //调用  
    e(1);  
  
    return 0;  
} 

具体的代码详见 :C++实现委托机制之完整代码实现(二)


C++ 并没有原生的委托机制,但可以通过函数指针和对象指针来模拟委托机制。 在 C++ 中,函数指针可以作为参数传递和返回值,可以使用函数指针来实现委托的功能。而对象指针可以用于保存对象的状态和数据,也可以用于调用对象的成员函数。 以下是一个简单的 C++ 示例代码,演示了如何通过函数指针和对象指针来实现委托机制: ```cpp #include <iostream> #include <functional> using namespace std; class Delegate { public: void (*func)(void*); // 函数指针 void* obj; // 对象指针 void Invoke() { func(obj); // 调用函数指针 } }; class Test { public: void Print() { cout << "Hello, world!" << endl; } }; void PrintTest(void* obj) { Test* test = (Test*)obj; // 将对象指针转换为 Test 类型指针 test->Print(); // 调用对象的成员函数 } int main() { Test test; Delegate delegate; delegate.func = PrintTest; delegate.obj = &test; delegate.Invoke(); // 调用委托 return 0; } ``` 在这个示例代码中,我们定义了一个 `Delegate` 类,其中包含了一个函数指针和一个对象指针。`Invoke` 函数用于调用委托。我们还定义了一个 `Test` 类,其中包含了一个成员函数 `Print`。在 `main` 函数中,我们创建了一个 `Test` 对象 `test`,并创建了一个 `Delegate` 对象 `delegate`,将 `PrintTest` 函数的地址和 `test` 对象的地址分别赋值给 `Delegate` 对象的函数指针和对象指针。最后,我们通过 `delegate.Invoke()` 调用委托,实际上就是调用了 `PrintTest` 函数,并将 `test` 对象传递给了该函数,从而调用了 `Test` 对象的 `Print` 成员函数。 需要注意的是,这只是一个简单的示例,实际的委托机制可能更加复杂,并且需要考虑线程安全等问题。如果需要使用委托机制,建议使用现有的 C++ 第三方库或框架,如 Boost、Qt 等。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值