• 系统程序员成长计划拥抱变化(下)


    转载时请注明出处和作者联系方式
    文章出处:http://www.limodev.cn/blog
    作者联系方式:李先静 <xianjimli at hotmail dot com>

    在专用双向链表中,dlist_printf的实现非常简单,如果里面存放的是整数,用”%d”打印,存放的字符串,用”%s”打印。现在的麻烦在于双向链表是通用的,我们无法预知其中存在的数据类型,也就是我们要面对数据类型的变化。怎么办呢?初学者常见的做法有:

    1.实现多个函数,需要哪个就用哪个。比如实现的有dlist_print_int用来打印存放整数的双向链表,dlist_print_string用来打印存放字符串的双向链表,如此等等,其它类型都有自己的打印函数。

    这种做法的缺点有:一是每个函数的实现方式类似,造成大量重复的代码。二是数据类型的种类不确定,每种数据类型都要写一个print函数,当要存放新的数据类型时,需要修改dlist的实现。

    2.传入一个附加参数来决定如何打印。比如传入1表示按整数方式打印,传入2表示按字符串方式打印,以此类推。

    这种做法比第一种好一点,至少不会造成大量重复的代码。但是同样存在增加新类型时要修改dlist_print函数的问题。

    3调用dlist的接口函数获取每一个位置的数据并打印出来。

    它可以避免前面两种方法的缺点,而且是一种很直观的方式。奇怪的是偏偏很少有人这样去做,原因可能有两个,其一是太拘泥于传统的实现方式而没有想到这一种。其二是担心性能问题,因为通过索引取值,每一次都从头开始定位,其性能开销为O(n*n)。

    其实这种方法是可以接受的,dlist_print是用于辅助测试,我们并不在乎它的性能开销,而且很少在链表中存放成千上万的数据,它带来的性能影响也没有想的那样严重。

    不过在这里我们要介绍一种新的方法:

    dlist_print的大体框架为:

        DListNode* iter = thiz->first;
    
        while(iter != NULL)
        {
            print(iter->data);
            iter = iter->next;
        }
    

    在上面代码中,我们主要是不知道如何实现print(iter->data);这行代码。可是谁知道呢?很明显,调用者知道,因为调用者知道 里面存放的数据类型。OK,那让调用者来做好了,调用者调用dlist_print时提供一个函数给dlist_print调用,这种回调调用者提供的函 数的方法,我们可以称它为回调函数法。

    调用者如何提供函数给dlist_print呢?当然是通过函数指针了。变量指针指向的是一块数据,指针指向不同的变量,则取到的是不同的数据。函 数指针指向的是一段代码(即函数),指针指向不同的函数,则具有不同的行为。函数指针是实现多态的手段,多态就是隔离变化的秘诀,这里只是一个开端,后面 我们会逐步的深入学习。

    回到正题上,我们看如何实现dlist_print:
    定义函数指针类型:
    typedef DListRet (*DListDataPrintFunc)(void* data);

    声明dlist_print函数:
    DListRet dlist_print(DList* thiz, DListDataPrintFunc print);

    实现dlist_print函数:

    DListRet dlist_print(DList* thiz, DListDataPrintFunc print)
    {
        DListRet ret = DLIST_RET_OK;
        DListNode* iter = thiz->first;
    
        while(iter != NULL)
        {
            print(iter->data);
    
            iter = iter->next;
        }
    
        return ret;
    }
    

    调用方法

    static DListRet print_int(void* data)
    {
        printf("%d ", (int)data);
    
        return DLIST_RET_OK;
    }
    …
    dlist_print(dlist, print_int);
    

    所有问题都解决了,是不是很简单? 我以前写过一篇关于函数指针的BLOG,文中声称不懂函数指针就不要自称是C语言高手,现在我仍然坚持这个观点。函数指针的概念本身很简单,关键在于灵活应用,这里是一个最简单的应用,希望读者仔细体会一下,后面将会有大量篇幅介绍。

    我写了一个简单的示例,它的实现并不完善,不过用来演示我们到目前为止学到的内容已经够了。有兴趣的读者请到这里下载。

  • 相关阅读:
    php 3des加密 兼容JAVA 多么痛的领悟呀
    主机序和网络序
    不用递归实现无限分类数据的树形格式化
    python学习笔记之open函数的用法
    据说是百度面试题(1)
    YII+DWZ三级城市联动挂件
    wpf 报错: 在 AddNew 或 EditItem 事务过程中不允许“DeferRefresh”。
    MVVM了解
    纪念2015年上半年
    c# 委托与事件
  • 原文地址:https://www.cnblogs.com/zhangyunlin/p/6167488.html
Copyright © 2020-2023  润新知