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

C++:实现委托机制 为了解决GUI 跨线程操作界面的导致主线程崩溃的情况, C++ 11 终于推出了自己的thread 类thread 类提供detach() 的方法, 让worker线程也具备了操作GUI的能力.在GUI开发中, 通过使用function和thread等C++ 11 新特性,能够更便捷的开发出高质量的程序, 同时回避开发过程中的问题。... 阅读详情

1.引言:

             如果你接触过C#,你就会觉得C#中的delegate(委托)十分灵巧,它的用法上和C\C++的函数指针很像,但是却又比C\C++的函数指针更加灵活。并且委托可以一对多,也就是可以注册多个函数,甚至是某个类的非静态成员函数。而实现事件消息机制【1】也十分依赖于委托机制。基于这样的目的,我们试着在C++上封装出这样的一个委托机制。


【1】值得注意的是这里的委托事件模式与Windows的消息循环体系是不同的,通常Windows的消息是放到消息队列中,应用程序进程从队列中得到消息,然后调用消息处理过程来处理消息,这里是真正的消息通知,并且消息处理过程是有固定的函数声明的,不能更改成其他的格式,但是委托事件模式实际上就是一次函数调用,委托事件模式的使用,其好处是在开发中可以像真正的消息事件体系一样来理解整个体系模式,可以做到很好的接口分离。


2.委托功能使用:

            委托使用简单,支持多播,可以添加删除委托。同时支持C++的普通函数、模板函数、类成员函数,类的静态成员函数,并且支持多态。

           我们来看一个简单的例子:

#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;
}

运行结果:


这里是普通函数 :1
这里是成员静态函数 : 1
这里是成员非静态函数 : 1


           由此可以看到将三个函数注册到委托中后,调用委托不仅三个函数不仅能够成功调用,而且参数也是成功传递的。


3.实现无返回值无参数委托的构造


         这一部分代码是参照http://blog.csdn.NET/gouki04/article/details/6852394这篇博客上写的。

          

          我们先来看C++中普通函数指针和成员函数指针的区别:

void NormalFunc()
{
	printf("这里是普通函数\n");
}

class A
{
public:
	static void StaticFunc()
	{
		printf("这里是成员静态函数\n");
	}
	void MemberFunc()
	{
		printf("这里是成员非静态函数\n");
	}
};
int _tmain(int argc, _TCHAR* argv[])
{
	//普通函数
	typedef void(*NormalFuncp)();
	//成员函数
	typedef void(A::*MemberFuncp)();

	NormalFuncp fun1 = NormalFunc;
	MemberFuncp fun2 = &A::MemberFunc;
	NormalFuncp fun3 = A::StaticFunc;
	A a;
	fun1();
	(a.*fun2)();
	fun3();

	return 0;
}
    可以看到普通函数指针调用函数的方式和成员非静态函数指针调用函数的方式不同,成员 非静态 函数指针调用函数需要依赖于该类的一个对象,并且用 .* 或者 ->* 的语法来调用。而成员静态函数调用方式却和普通函数差不多。所以我们需要创建一个委托的基本接口对于不同类型指针的再来派生多态处理。

class IDelegate
{
public:
    virtual ~IDelegate() { }
    virtual bool isType(const std::type_info& _type) = 0;
    virtual void invoke() = 0;
    virtual bool compare(IDelegate *_delegate) const = 0;
};

这里定义了三个接口,一个是调用,表示调用该Delegate对应的函数指针指向的函数。剩下两个是类型判断,使用了C++的RTTI,动态类型的判断。

接下来我们来派生出能注册普通函数的委托。

class CStaticDelegate : public IDelegate
{
public:
    typedef void (*Func)();

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

    virtual bool isType(const std::type_info& _type) { return typeid(CStaticDelegate) == _type; }

    virtual void invoke() { mFunc(); }

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

private:
    Func mFunc;
};

   然后是可以注册指向成员非静态函数的指针的委托,因为指向成员非静态函数的类别是这样的  void (ClassName::*FuncName)();而ClassName又是不确定的所以我们这里要使用模板类来封装:

template<class T>
class CMethodDelegate : public IDelegate
{
public:
    typedef void (T::*Method)();

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

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

    virtual void invoke()
    {
        (mObject->*mMethod)();
    }

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

private:
    T * mObject;
    Method mMethod;
};

这里的类型T是指这个委托注册的成员函数指针所属的类的类别。比如我注册 A::&MemberFunc ,那么这里的T就被替换为A.

          其实大家仔细看代码可以发现这两个类十分相似只是invoke() 里面调用的方式不同。还有这里的compare判断是指看两个委托指向的成员函数和对象是否一样,如果只是成员函数一样,绑定的对象不一样也视作不同的委托。

         这样我们就把C++中的无返回值、无参数的普通函数指针、成员函数指针封装好了。
         

          最后提供统一的接口去生成”函数指针对象“

inline IDelegate* newDelegate( void (*_func)() )
{
    return new CStaticDelegate(_func);
}

template<class T>
inline IDelegate* newDelegate( T * _object, void (T::*_method)() )
{
    return new CMethodDelegate<T>(_object, _method);
}

  最后我们我们实现委托,这里我们对多个函数指针的存储使用了STL的list.所以头文件中需要引入<list>

class CMultiDelegate
{
public:
    typedef std::list<IDelegate*> ListDelegate;
    typedef ListDelegate::iterator ListDelegateIterator;
    typedef 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) = 0;
            }
        }
    }

    CMultiDelegate& operator+=(IDelegate* _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& operator-=(IDelegate* _delegate)
    {
        for (ListDelegateIterator iter=mListDelegates.begin(); iter!=mListDelegates.end(); ++iter)
        {
            if ((*iter) && (*iter)->compare(_delegate))
            {
                if ((*iter) != _delegate) delete (*iter);
                (*iter) = 0;
                break;
            }
        }
        delete _delegate;
        return *this;
    }

    void operator()( )
    {
        ListDelegateIterator iter = mListDelegates.begin();
        while (iter != mListDelegates.end())
        {
            if (0 == (*iter))
            {
                iter = mListDelegates.erase(iter);
            }
            else
            {
                (*iter)->invoke();
                ++iter;
            }
        }
    }

private:
    CMultiDelegate (const CMultiDelegate& _event);
    CMultiDelegate& operator=(const CMultiDelegate& _event);

private:
    ListDelegate mListDelegates;
};

        其实最后这个类很像是一个指针容器,然后各个成员方法也只是对这个容器里面的对象进行管理。而主要的三个方法:

          重载了 +=  表示向这个委托注册一个函数指针,这个方法会自动判重,如果重复了就不会向里面添加。

          重载了  -=  表示向这个委托注销一个函数指针,如果这个函数指针不存在就什么也不执行。

          重载了  ()   表示当作函数调用启动这个委托,内部就是将所有函数指针指向的函数都运行一遍。

       

          到这里,基本上无返回值、无参数的委托就封装好了。我们先来测试一下:


void Say()
{
	printf("你好\n");
}
class A
{
public :
	void Say(){ printf("你不好\n"); }
};
int _tmain(int argc, _TCHAR* argv[])
{
	CMultiDelegate onclick;
	onclick += newDelegate(Say);

	onclick += newDelegate(&A(),&A::Say);   //注意这里不能传入 new A(), 因为会内存泄漏。

	onclick();

        return 0;
}

  如果以上代码能够成功运行,那么说明你的第一个版本的委托已经封装完毕,但是如何实现任意返回值、任意参数类型、任意参数个数的函数指针的委托呢?

          我在网上查阅过许多代码,发现大多数都是使用的宏替换加上多次引用头文件使得每次编译不同参数个数版本的委托,但是这个方法我感觉巧妙但却鸡肋。后来我尝试着使用C11的新特性:可变模板参数实现了这个需求。能够对用户定义的不同委托去自动生成对应的函数指针类型的委托类。


    具体的代码详见 C++实现委托机制之完整代码实现(一)
C++笔记之委托 委托delegation)是一种将任务或行为转交给另一个对象或函数处理的技术。把某些操作的实现“托付”给别人完成。经典的23种设计模式创建型(5种):单例、工厂方法、抽象工厂、建造者、原型结构型(7种):适配器、桥接、组合、装饰器、外观、享元、代理行为型(11种):责任链、命令、解释器、迭代器、中介者、备忘录、观察者、状态、策略、模板方法、访问者。 阅读详情

相关推荐

C++中实现委托(delegate)

网上有很多关于C++ delegate机制的文章,但都讲的是函数指针的内容,上周就C++中实现C#的delegate机制的问题研究了好几天,查看了很多相关资料,才解决了问题,现将我写的C++ delegate测试程序贴出来,希望能帮到有需要的人。

【项目三】C++实现事件委托

前几天在学习设计模式中的观察者模式时发现需要用到一个十分巧妙的机制——事件委托,事件委托的匿名属性,可以使得通知者对象更新观察者对象时不需要依赖具体名称的函数,是一种降低系统耦合度的设计。我看的设计模式书是《大话设计模式》,里面使用C#举例的,C#中就有事件委托的机制,而我是学C++,无奈C++中没有事件委托的设计,虽然Qt中有类似的机制——信号与槽,但是总感觉技术不如掌握在自己手中好,索性自己了解并编写一套事件委托系统

学艺不精的Anton的博客 1409

5分钟搞定!用Keil MDK将STM32F103C8T6工程无缝迁移到ZET6开发板

本文详细介绍了如何在Keil MDK环境下快速将STM32F103C8T6工程迁移到ZET6开发板,涵盖硬件差异对比、工程配置调整、外设适配及编译排错等关键步骤,帮助开发者5分钟内完成工程移植,大幅提升开发效率。

weixin_30879169的博客 406

【C++】C++中的委托构造函数详解

避免你有多个参数表不同但是逻辑相近(或者有公共部分)的构造函数的时候,一个逻辑写好几遍造成代码重复。Q:执行的顺序?A:先执行目标构造函数,再执行委托构造函数。

qq_35902025的博客 9940

Pi0 项目复现及项目问题解决方案(全网抢先发布~)

Pi0 项目复现及项目问题解决方案(全网抢先发布~)

m0_68167062的博客 2260

XGBoost详解(原理篇)

XGBoost全称为eXtreme Gradient Boosting,即极致梯度提升树。XGBoost是Boosting算法的其中一种,Boosting算法的思想是将许多弱分类器集成在一起,形成一个强分类器(个体学习器间存在强依赖关系,必须串行生成的序列化方法)。Note:关于Boosting算法详见博文集成学习详解_tt丫的博客-优快云博客XGBoost是一种提升树模型,即它将许多树模型集成在一起,形成一个很强的分类器。其中所用到的树模型则是CART回归树模型。

tt丫的博客 7万+

关于c++委托机制的实现(一)

写自己的bug,让自己无bug可写 下一篇传送门:https://blog.youkuaiyun.com/qq_34236354/article/details/89878858 本博客参考其他博客编写而成,参考博客地址:https://blog.youkuaiyun.com/gouki04/article/details/6852394,有兴趣的可以去看看 函数简介:c++中有普通函数,类静态成员函数,类普通成员...

qq_34236354的博客 251

华为ENSP虚拟网络与本地物理网卡高效桥接实战指南

本文提供了一份详细的华为ENSP虚拟网络与本地物理网卡桥接实战指南。通过清晰的四步操作,帮助用户打破虚拟实验环境的“孤岛”限制,实现ENSP设备与真实局域网及互联网的互通,从而进行更真实的协议测试、软件升级和混合网络环境搭建。

qsc90123456的博客 536

C++实现的委托机制(一)

C++实现的委托机制(一) 作者:gouki04(啊古)  撰写日期:2011-09-27 博客链接:http://blog.youkuaiyun.com/gouki04  1.引言     下面的委托实现使用的MyGUI里面的委托实现,MyGUI是一款强大的GUI库,想理解更多的MyGUI信息,猛击这里http://mygui.info/ 。最终的代码可以在这里下载:http://downloa

659

第8章 - 异构系统的协同控制及最优控制 --> 最优控制

异构系统的协同控制及最优控制 --> 协同控制

赵继超的笔记 2259

Windows 上 openclaw onboard --install-daemon 命令的安装位置和启动配置

摘要:Windows系统中,执行openclaw-cnonboard--install-daemon命令会创建计划任务OpenClawGateway,该任务在用户登录时自动运行位于%USERPROFILE%.openclaw\gateway.cmd的脚本文件来启动服务。管理方法包括通过任务计划程序或命令行(schtasks)查看/启停任务,使用openclaw gateway命令操作服务,或通过删除计划任务和脚本文件完全卸载服务。该机制确保OpenClawGateway服务随系统登录自动启动。(149字)

大坡3D软件开发 1369

C++实现委托机制之完整代码实现(一)

头文件 MyDelegate.h 代码如下: #include #include #include class IDelegate { public: virtual ~IDelegate() { } virtual bool isType(const type_info& _type) = 0; virtual void invoke() =

起个名字好难啊 449

机器学习项目实战案例数据集

本专栏整理了《机器学习项目实战案例》,内包含了各种不同的机器学习项目,包含项目原理以及源码,每一个项目实例都附带有完整的代码+数据集。

优快云 精品推荐 3633

磁盘调度算法实验:FCFS、SSTF与SCAN的对比分析与实现

磁盘I/O性能是影响操作系统整体响应速度的关键因素之一,而磁盘调度算法则直接决定了磁头寻道的效率。在多道程序环境下,多个I/O请求同时到达,如何选择服务顺序、最小化寻道时间,是操作系统存储管理中的核心问题。FCFS、SSTF和SCAN是三种最经典的调度策略,分别代表了公平、贪心和方向感知的不同设计思想:FCFS按到达顺序服务,简单却可能造成磁头频繁摆动;SSTF优先满足距离最近的请求,缩短总寻道长度但存在饥饿风险;SCAN引入方向变量,模拟电梯运行,在效率与公平之间取得平衡。理解这些算法的原理与差异,不仅有

weixin_34268169的博客 318

【STK】手把手教你利用STK进行光电红外仿真04-STK/EOIR模块的实战操作1航天器星载相机对LEO卫星成像

今天引入一个实际的场景,帮助大家完成对于这个模块的理解与运用。

weixin_44184852的博客 2417

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

c++实现c#委托机制

看了两本关于设计模式的书,一本是用的java语言描写的《设计模式之禅》,另一本是用C#语言描述的《大话设计模式》,两本都不是我常用的C/C++,不过C++是面向对象的语言,当然你也可以用它编写面向过程的程序。        既然C++是面向对象的语言,那么所有的两本书讲的所有的设计模式都应该能够实现,只是实现的具体方法不一样而已,比如一个单例模式,用java实现的方法如下:     class

SunminpSun的专栏 891

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

杂货边角(9):C++实现事件委托机制

在设计模式中,根据耦合紧密度可以得到排序为:继承 > 依赖 > 组合/聚合 > 委托。其实个人感觉,事件委托机制说白了就是一个函数指针数组,通过注册绑定的函数,然后在特定的事件发生后触发遍历执行所有注册的函数,机制其实和C++运行库glibc中crtbegin.o和crtend.o收集当前程序中所有全局对象的初始化函数和析构函数,用以执行遍历操作一致。所以归根结底,事件委托机制的关键便是函数指针注册

墨篙和小奶猫 647

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

C++实现的委托机制(2)

C++实现的委托机制(2) 1.实现任意参数的函数委托 按上一篇文章的方法,你已经可以使用无参数的函数委托了。当然,这远远不够。要实现任意参数的函数委托,这里的任意参数包括任意个数和任意类型。任意类型这个容易解决,使用模板就行,但任意参数个数呢? 注:最终的实现代码可以

古的博客 2227

C++学习笔记38:事件机制

事件基本概念 操作系统或应用程序内部发生某件事,程序的某个组件需要响应该事件,并进行特定处理 面向对象架构中,事件响应函数最可能为成员函数 问题:指向类成员函数的指针不能转换为哑型指针void *,也不能随意转换为指向另一个类的成员函数的指针 解决方案:使用指向指向类成员函数的指针的指针 实现策略:事件委托模型 Event类模板:管理事件响应者对象,实现事件多播 ...

weixin_30530339的博客 295

成员函数指针与高性能的C++委托(上篇)

Member Function Pointers and the Fastest Possible C++ Delegates撰文:Don Clugston翻译:周翔

son_of_god的专栏 507

c++面向对象高级编程 学习五 组合、委托与继承

组合 composition 表示has a queue类中有一个deque容器,这种关系叫做 组合 queue中的六个函数都是调用c的函数完成的 template <class T> class queue { ... protected: deque<T> c; // 底層容器 public: // 以下完全利用 c 的操作函數完成 bool empty() con...

weixin_43116900的博客 470

C++11 FAQ中文版:委托构造函数(Delegating constructors)

二25 Year 2011陈 良乔C++11 FAQ 委托构造函数(Delegating constructors) 在C++98中,如果你想让两个构造函数完成相似的事情,可以实现两个完全相同的构造函数,或者是实现一个init()函数,两个构造函数都调用这个init()函数。例如: class X { int a; // 实现一个初始化函数

飞空静渡 6447
上一篇: XML 创建多个Element的XML文件
下一篇: C++实现委托机制之完整代码实现(一)
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值