zoukankan      html  css  js  c++  java
  • 系统程序员成长计划拥抱变化(下)

    转载时请注明出处和作者联系方式
    文章出处: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语言高手,现在我仍然坚持这个观点。函数指针的概念本身很简单,关键在于灵活应用,这里是一个最简单的应用,希望读者仔细体会一下,后面将会有大量篇幅介绍。

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

  • 相关阅读:
    一个简单的knockout.js 和easyui的绑定
    knockoutjs + easyui.treegrid 可编辑的自定义绑定插件
    Knockout自定义绑定my97datepicker
    去除小数后多余的0
    Windows Azure Web Site (15) 取消Azure Web Site默认的IIS ARR
    Azure ARM (1) UI初探
    Azure Redis Cache (3) 创建和使用P级别的Redis Cache
    Windows Azure HandBook (7) 基于Azure Web App的企业官网改造
    Windows Azure Storage (23) 计算Azure VHD实际使用容量
    Windows Azure Virtual Network (11) 创建VNet-to-VNet的连接
  • 原文地址:https://www.cnblogs.com/zhangyunlin/p/6167488.html
Copyright © 2011-2022 走看看