系统程序员成长计划-Write once, run anywhere(WORA)(下)
转载时请注明出处和作者联系方式
文章出处:http://www.limodev.cn/blog
作者联系方式:李先静 <xianjimli at hotmail dot com>
1.专用链表和通用链表各自的特点与适用范围。
专用链表在这里是指它的实现和调用耦合在一起,只能被一个调用者使用,而不能单独在其它地方被重用。通用链表则相反,它具有通用性,可以在多处被重 复使用。尽管通用链表相对专用链表来说有很多优越之处,不过简单的断定通用链表比专用链表好也是不公正的,因为它们都有自己的优点和适用范围:
专用链表的优点:
更高性能。专用链表的实现和调用在一起,可以直接访问数据成员,省去了包装函数带来的性能开销,可以提高时间性能。专用链表无需实现完整的接口,只要满足自己的需要就行了,生成的代码更小,因此可以提高空间性能。
更少依赖。自己实现不用依赖于别人。有时候你要写一个规模不大的跨平台程序,比如想在展讯手机平台和MTK手机平台上运行,虽然有现存的库可用,但你又不想把整个库移植过去,那么实现一个专用链表是不错的选择。
实现简单。实现专用链表时,不需要考虑在各种复杂应用情况下的特殊要求,也不需要提供完整的接口,所以实现起来比通用链表更为简单。
通用链表的优点(从全局来看):
可靠性更高。通用链表的实现要复杂得多,复杂的东西意味着不可靠。但它是可以重复使用的,其存在的问题会随每一次重用而被发现和改正,慢慢的就行成一个可靠的函数库。
开发效率更高。通用链表的实现要复杂得多,复杂的东西也意味着更高的开发成本。同样因为它是可以重复使用的,开发成本会随每一次重用而降低,从整个项目来看,会大大提高开发效率。
考虑到链表是最常用的数据结构之一,很多地方都会用到它,实现通用的链表会更有价值。接下来我们要实现一个通用的链表,不过请大家记住,实现通用的 链表并不是我们的目标,而是我们学习软件设计方法的手段。前面我许诺过要以简单的数据结构讲述复杂的软件设计方法,链表就是其中的载体之一。
2.如何编写一个通用的链表?
编写通用链表是一项复杂的任务,不可能在这一节中把它阐述清楚,这里我们先考虑三个问题:
存值还是存指针
通用链表首先是要做能够存放任何数据类型的数据,新手常见的做法是定义一个抽象数据类型,需要什么存放什么就定义成什么。如:
typedef int Type; typedef struct _DListNode { struct _DListNode* prev; struct _DListNode* next; Type data; }DListNode;
这样的链表算不上是通用的,因为你存放整数时编译一次,存放字符串时,重义Type再编译一次,存放其它类型同样要重复这个过程。麻烦不说,关键是 没有办法同时使用多个数据类型。我们要找到一种同时可以表示不同数据类型的类型才行,有人说可以用union,但是数据类型是无穷无尽的,不可能在 union中表示它们的全部。
可行的办法有两种:
存值:
typedef struct _DListNode { struct _DListNode* prev; struct _DListNode* next; void* data; size_t length; }DListNode;
存入时拷贝一份数据,保存数据的指针和长度。考虑到拷贝数据会带来性能开销,不合符C语言的风格,而且C语言中没有构造函数,实现深拷贝比较麻烦,所以在C语言中以这种方式实现的链表很少见。
存指针:
typedef struct _DListNode { struct _DListNode* prev; struct _DListNode* next; void* data; }DListNode;
只是保存指向对象的指针,存取效率高,是C语言中常见的做法。在存放整数时,可以把void*强制转换成整数使用,以避免内存分配(在现实中,90%以上的情况,链表都是存放结构的)。
让C++可以调用
这不是一个重要的话题,只是顺便提一下。C++中允许同名函数存在,所以编译器会对函数名重新编码。C++代码包含C语言的头文件时,重新编码名字 与C语言库中的原函数名不一致,结果造成找不到函数的情况。为了让C语言实现的函数在C++中可以调用,需要在头文件中加点东西才行:
#ifdef __cplusplus extern "C" { #endif … #ifdef __cplusplus } #endif
它表示如果在C++中调用这里的函数,编译器不能对函数名进行重新编码。
完整的接口
作为一个通用的链表,接口要比较完整才行,否则无法满足各种情况的需要(提供完整的接口并不违背最小接口原则)。实现具有完整接口的链表不是件容易的事,读者先实现插入删除等基本操作就行了,后面我们会慢慢扩展它的功能。
(为了避免读起来拗口,本文把双向链表简写成链表了,希望读者不要介意)