当前位置:首页 > 芯闻号 > 充电吧
[导读]用过C#的人,一般都知道委托和事件。如果没有用过C#,我在这里简单解释一下委托,如果用过了,下面可以skip。委托是一个方法声明,我们知道,在C语言中,可以通过函数指针表示一个地址的CALL方法,委托

用过C#的人,一般都知道委托和事件。
如果没有用过C#,我在这里简单解释一下委托,如果用过了,下面可以skip。

委托是一个方法声明,我们知道,在C语言中,可以通过函数指针表示一个地址的CALL方法,委托在C#中也差不多是干这样的工作。
但是委托有一些不同,主要的地方就是,在C++中,函数指针并不是“面向对象”的,比如,我们有一个类CTest,类中有一个成员方法foo,此时如果我们要通过函数指针的方式来调用foo的话,因为foo是类CTest的成员,我们需要CTest的实例this指针。

比方说:

class CTest
{
    public:
        void foo(int i) {}
};

它的成员函数指针应该这样写:

void (CTest::*FuncFoo)(int);

如果这个成员是const修饰的,则这样:

void (CTest::*FuncFoo)(int) const;


调用怎么办呢?这样:

CTest* pThis = new...;
FuncFoo pThis_foo = &CTest::foo;
(pThis->*pThis_foo)(123);

如果你这样写:

pThis->*pThis_foo(123);

是会出问题的,因为->*这个运算符的优先级很低。。。。

如果CTest不是在HEAP上的,是在stack上的呢?
这样:

CTest pThis;
(pThis.*pThis_foo)(123);


这些奇葩的语法,->*、.*这种神奇的运算符真是蛋疼。
而有时候我们又无法逃避,比如启动线程的时候,我们一般从一个static函数通过这种sb的语法,把类this当作参数传过去,然后跑过去类的成员函数。

但是在C#中,委托则是面向对象的,你可以用委托表示一个类实例的成员函数指针。
比如:

namespace ConsoleApplication1
{
    public delegate void CallbackFunc(int i);
    //void (callback*)(int i);

    class CNewTest
    {
        private static readonly string _str = "this CNewTest.";

        public void MemberCallback(int i)
        {
            System.Diagnostics.Debug.WriteLine(i.ToString());
            System.Diagnostics.Debug.WriteLine(_str);
        }
    }

    class Program
    {
        static void StaticCallback(int i)
        {
            System.Diagnostics.Debug.WriteLine(i.ToString());
        }

        static void Main(string[] args)
        {
            CallbackFunc StaticCbF = new CallbackFunc(StaticCallback);
            StaticCbF(123); //Call to static void StaticCallback(int i)

            CNewTest c = new CNewTest();
            CallbackFunc MembCbF = new CallbackFunc(c.MemberCallback);
            MembCbF(1234); //Call to c->MemberCallback

            Console.ReadKey();
        }
    }
}

输出:
123
1234
this CNewTest.

也就是,在C#中,只要方法适应委托声明,那都可以call过去,C#为我们自动隐藏了this指针。
所以,就在C#中诞生了事件,一个提供者对象通过定义一个委托,然后把委托声明为事件,此时其他监听者对象就可以把这个事件跟自己的成员函数挂钩上来,然后等待提供者在需要的时候调用这个委托,则调用到了成员函数,就是触发了事件。

为什么声明为事件,直接暴露委托不是可以了么?
这是一个逻辑问题,如果直接暴露委托,那订阅者是可以主动触发委托的,订阅者可以是主动的,还“订阅”个屁啊。
如果用事件,则订阅者只能等待提供者来触发委托,此时订阅者,只能是“被动”的。

这个概念,用C++的来理解,也就是:
类A有一个public的ptr、ptr_this,此ptr保存了一个成员函数指针,ptr_this保存ptr的类实例。
类B把这个ptr改成他自己的,ptr_this改成它的this,适应这个成员函数指针的声明。
类A在某些工作搞定后,检查一下ptr != nullptr,然后call这个ptr,就调用到了类B的成员函数。

但是C++做这些有天然的不足,ptr不是nullptr倒是可以,但是可能类B早被delete了,但是存储在你类A的ptr依然不是nullptr,你call直接挂,当然用C11的shared_ptr、weak_ptr这种东西,倒是也是一个解决方案,不过总的来说,就是不爽,这得多麻烦?
也就是C++,我们还是难以跳出手动管理内存这个圈子。
C#的GC自动管理则完全通杀这些问题。

还有一个就是,C#中,委托是一个多播的实现,比如:

        static void Main(string[] args)
        {
            //CallbackFunc StaticCbF = new CallbackFunc(StaticCallback);
            //StaticCbF(123); //Call to static void StaticCallback(int i)

            //CNewTest c = new CNewTest();
            //CallbackFunc MembCbF = new CallbackFunc(c.MemberCallback);
            //MembCbF(1234); //Call to c->MemberCallback

            CallbackFunc cbF = new CallbackFunc(StaticCallback);
            CNewTest c = new CNewTest();
            cbF += c.MemberCallback;
            cbF(123);

            Console.ReadKey();
        }

输出:
123
123
this CNewTest.

简单的说,就是cbF这个玩意儿里面,有一个泛型的List。。。。在Call的时候,是跑表来一个个call的,十分方便。
说白了,委托就是一个【方法的接口】,只要方法对就行,安全又舒服。
于是委托+事件这种多播实现,就能很好的做出一个观察者模式。

那么多弊端出来了,C11委员会也发现了这些问题,就加入了一个std::function,这个东西呢,说简单了,它就是差不多用来实现委托的功能的(不是实现事件哦)。
还是回到上面那个C#写的ConsoleApplication1,我们用std::function重来一遍。
(编译环境为VS2013,Windows SDK。)

使用std::function前,需要包含#include

本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读

其实在 c++语言里面const修饰的才算是一个真正的常量,在 c 语言中 const 可以说是个“冒牌货”。为什么会这样?其实是 c++ 编译器对 const 进行了加强,当 c++ 编译器遇到常量声明时,不会像 c...

关键字: c++ C语言 const

返回函数的引用去初始化一个新的引用这个和前面一样,都是不会产生副本,但是现在是用返回值去初始化一个引用声明c,也就是说这时候变成了变量temp的别名,在c的生命周期内temp是一直有效的,这样做完全可以。

关键字: c++ 返回值 引用声明

C++是一种面向对象的高级程序设计语言,是C语言的超集。

关键字: c++ C语言

目前主流的面向对象的编程语言C#、C++、Java; 一、C#的特点: C#在带来对应用程序的快速开发能力的同时,并没有牺牲C与C++程序员所关心的各种特性。它忠实地继承了C和C++的优点。如果你对C

关键字: c# 面向对象编程

分析:这是Adobe 公司2007 年校园招聘的最新笔试题。这道题除了考察应聘者的C++ 基本功底外,还能考察反应能力,是一道很好的题目。 在Java 中定义了关键字final ,被final 修饰的

关键字: c++ class

泛型算法中的定制操作很多算法都会比较输入序列中的元素,通过定制比较动作,可以控制算法按照编程者的意图工作。本文以string排序为例进行说明,首先是缺省的排序动作: vector v{"This","

关键字: c++

为什么是lambda?讲了这么多天的lambda表达式,有一个很基本的问题没有回答:为什么叫lambda表达式呢?首先这个lambda就是罗马字母λ,lambda表达式即λ表达式。数学上有一个概念叫λ

关键字: c++

        假设我们有个函数用来揭示处理程序的优先权,另一个函数用来在某动态分配所得的Widget 上进行某些带有优先权的处理:int priority () ; void processWi

关键字: c++ effective

判断链表中是否有环最经典的方法就是快慢指针,同时也是面试官大多想要得到的答案。       快指针pf(f就是fast的缩写)每次移动2个节点,慢指针ps(s为slow的缩写)每次移动1个节点,如果快

关键字: c++ 链表 快慢指针

转载请注明出处:http://blog.csdn.net/callon_h/article/details/52073268 引子 上一篇博客从内核驱动到android app讲述了android通过

关键字: c++ java
关闭
关闭