posts - 8, comments - 42, trackbacks - 0, articles - 0
  博客园 :: 首页 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理

公告

委托学习(1)-------函数指针的改头换面

Posted on 2006-07-10 17:35 李玉锋 阅读(...) 评论(...) 编辑 收藏
   又是提起委托,我在想的是众人必定皆恼怒,有关这个东西的谈论网上已悠悠不尽,何况我也不是赶时髦,追潮流的人,因此必然无法标新立异;只是把我学习的一些感受作以记录,便于日后回忆,也愿对各位朋友有点帮助。

对于学习C/C++语言的朋友,函数指针相信都不会觉得遥远和陌生吧,不管你对它是恨是爱,还是它令你忧伤令你逍遥自在;如果现今你还在使用C/C++,驰骋在Win32平台下怡然自乐,那么有它相伴身边你肯定不会觉得寂寞;如果你已告别C/C++,徜徉在.NET平台下悠然自得,那么没有了它相伴你也肯定不会觉得失落,因为一个名曰委托(Delegete)的朋友已翩然来到我们身边,所以我们用不着遗憾,更用不着千年一叹,因为委托可以称得上是函数指针的改头换面。

地球人都知道,函数指针的主要作用就是用它来调用函数的,那一个函数好端端的为什么不用人家的名字直接调用,偏偏的拐个弯用指针来调用呢?当然普通的调用直接用名字就可以了,但当我们想把函数作为参数来传递,就得靠函数指针;还有在程序界大名鼎鼎的回调函数(CallBack Function),它风光的背后也是函数指针在默默奉献。那么在.NET托管环境下,微软狠心割舍了函数指针这一不求名利的功臣,但没让我们失望的是增添了委托这一良将,把函数指针的功能发挥的淋漓尽致。还是来回忆一下我们的老朋友函数指针,正好这几天我也正在实行一惊天计划,准备对我暗恋已久的一女孩说出心里话,无奈我没经验又害羞,多次欲言却又口难开,所以准备托一好友帮忙先给她点暗示,完了我再随后出击,借写本文机会来场纸上谈兵。首先用函数指针模拟一下这个计划,妥当与否,姑且不作理论。

class liyufeng

{

       //注意,此处必须声明为静态方法。

public:

        static void mySay()

        {

                //我准备对女孩说的话,苦思冥想不知作何言语,各位有经验的朋友请指点一二,万分感激

                printf("I Like You,Do you knowif you dont know,now I must tell you ,tell you!");

        }

};

上面这个类用我大名的拼音来命名,算是代表我这个无缘爱情的苦命人。我要说的话由静态方法mySay来说出,此处只是输出到控制台,要是真能对人家说了就高兴死俺了。

class goodfriend

{

//这里的声明就是我们熟悉的函数指针了,我说话的那个mySay方法就由这个函数指针代劳,传递给朋友的friendSay方法

typedef void (*middleSay)(); 

//这里利用函数指针把函数作为参数传递,朋友的friendSay方法说话,就是函数指针middleSay说话,到最后其实就是我自己的mySay方法说话

public:

        static void friendSay(middleSay say)

        {

                say();

        }

};

上面的这个类代表我的好朋友,姓名就不透露了,免得太多的像我这样的不敢对女孩表白的人都去找他这个爱管闲事的家伙,哦,他可就忙不过来啦。我的话呢通过这里的friendSay方法逐句逐字的说给女孩听。朋友代表我和女孩见面的地点定在一个美丽的公园里。

class beautifulpark

{

        //注意:如果在Visual2005里建立一个Win32的控制台程序的话,主函数是不应该封装在类里的,在此只是为了说明和同下边的委托版本保持一致的需要。嘿嘿!总得找个约会地点嘛;运行本程序的话,下边主函数中的内容直接拷贝即可

int _tmain(int argc, _TCHAR* argv[])

{

        int c;

        //把俺说话的mySay方法传递给朋友的friendSay方法,关键是这个函数指针哦,朋友的friendSay方法才能接受俺的mySay方法(其实关键是一顿美味可口的饭菜)

                goodfriend::friendSay(liyufeng::mySay);

                scanf_s("%d",&c);

                return 0;

}

}

我和朋友一起先到公园,小心翼翼的把我说话的那个方法mySay给了朋友的friendSay方法,为什么要小心?函数指针嘛,不是个省心的东西,当然得小心喽。好了,后边的事情我就不得而知了,天知道女孩听了俺的话作何反应。

   我的话说完了,同时和大家一起回忆了函数指针,没经历过亘古悠香的C/C++语言而是直接和.NET下语言做亲密接触的朋友,看了上面的几行代码有没有感觉似曾相识?没有的话,下面我再来模拟一下C#委托版本的。

    class liyufeng

    {

        //注意,此处可以不声明为静态方法

        public static void mySay()

        {

            //嘿嘿!到这里时,又是斟酌了半天,还是没有想到说什么好

            System.Console.WriteLine("I Like You,Do you know,if you don’t know,now I must tell you ,tell you!");

        }

    }

这个liyufeng类和上面的C++版本的不同的只是输出到控制台用的方法不一样。

    class goodfriend

    {

 //这里使用delegate关键字声明.NET下的委托类型,同样,我说话的那个mySay方法就由这个委托代劳,传递给朋友的friendSay方法

        public delegate void middleSay();

//这里利用委托把函数作为参数传递,朋友的friendSay方法说话,就是委托middleSay说话,到最后其实就是我自己的mySay方法说话

        public static void friendSay(middleSay say)

        {

            say();

        }

    }

   我的好朋友goodfriend类和前边的C++版本非常相似,唯一不同的是指针换成了委托,但完成的是同样的功能。

class beautifulpark

    {

        static void Main(string[] args)

        {

//把俺说话的mySay方法传递给朋友的friendSay方法,关键是这个委托哦,朋友的friendSay方法才能接受俺的mySay方法(其实关键还是一顿美味可口的饭菜)

            goodfriend.friendSay(liyufeng.mySay);

            System.Console.Read();

        }

    }

好了,我追求缘分的计划也用委托班门弄斧的模拟了一番,到此为止,熟悉函数指针还没接触过委托的朋友,还有熟悉委托却对函数指针陌生的朋友,回头看一下,函数指针和委托是否颇为相似?在我初次接触委托的时候,也是被这个家伙搞的糊里糊涂,后来明白了,在.NET托管环境下的相关语言都是以面向对象作为基础来构建设计,一切都是对象,那么在这个对象丛生的世界里,摆脱对象这件外衣的成员是不允许存在的,也就是所有的成员都是从对象的老祖先Object派生而来。指针作为C/C++中一种特殊的数据类型,如果想在托管环境下生存的话,必然也是Object的后代,所谓入乡随俗,自然的也得披上对象的行套;所以对于函数指针而言,便是以一个对象封装了我们想要调用函数的所有相关信息,可以肯定这其中包含我们要调用函数的地址信息。当然C#等语言也只是对函数指针进行了类型化,给它改了个头,换了个面,起了个名叫Delegate,中文唤作委托;而对于其它数据类型的指针,并没有这么做,因此也就意味着我们是不可以像在C/C++中那样,自由自在的随意操作内存,这都是出于安全性的考虑。其实我觉得“委托”这个词起初理解起来真是有点让人抓耳挠腮的,想一下,你第一眼看到这个词什么感觉?能一下子就想到它是做函数调用的吗,能感觉的到它和函数指针有渊源吗?记得孟老大在一篇文章中谈到过“指针”这个词有误引理解思路之嫌,嘿嘿!有点跑题了。

    指针的本质我们都知道,就是其所指向成员的内存地址,函数指针就是要调用函数的内存地址了,地址就是赤裸裸的地址,除了这一地址信息之外其它的一无所有,如关于参数和返回值等的数据类型信息,而委托就不同,委托并不单纯的是调用函数的地址,它本质上是一个类,经过对函数指针的这么一类型化的封装,包含进了数据类型等很多信息,因此委托被称之为类型安全的指针。青出于蓝胜于蓝,委托还有比函数指针强大的地方,回头看一下前面C++的一行代码:

//注意,此处必须声明为静态方法。

public:

        static void mySay()

        {}

为什么必须声明为静态方法?再看一下C#的代码:

//注意,此处可以不声明为静态方法

        public static void mySay(){}

为什么这里可以不声明为静态方法?这里我们可以清楚的是,C++中用函数指针来调用的函数必须是静态的(C语言里这点无关紧要),而在C#中用委托来调用的函数可以是静态的,也可以不是.还不仅仅如此,函数指针一次只能实现对一个函数的调用,而委托没有这个限制,可以实现一次调用多个函数.这次就要到这里了,简单的写了一些自己对委托的理解,发现写文章的确是辛苦哦,不管是写好写坏,都是一种学习,一种对自己负责任的学习.后面的文章我将尝试讨论委托背后的实现机制,加深对其的理解,看一下委托究竟是如何构造出来的.