通用数据结构设计--单向链表(1)

引言

  我们的目标是设计一个优秀的通用单向链表,什么叫优秀?我也说不上来,但是我们可以去做,像写小说一样,我们想写出优秀的小说,必须需要看大量的其他优秀的小说,这是第一步,吸收它的优点,这是第二步,吸收后灵活使用在自己的小说里,这是第三步。
  有些人对写代码没有更高的追求,往往在看懂其他人的代码,并不会进行深入的第二步和第三步,这样看代码只会学到一些代码中亮眼的一些设计,也就是学到一些皮毛,而我的数据结构的设计会学习其他人的设计,提取出精华,分析出他的设计思维,再将大量的精华写入到我们的设计中,即是表现出对软件设计敬畏和对软件设计专业严谨的态度。

通用数据结构

  通用数据结构即可以存取任意类型的数据,无论是基本数据类型或者是自定义的结构体。

深和浅

  大家可能都听说过,深拷贝浅拷贝,java中有这样的概念,它定义,拷贝a对象时,使用直接A b = a;这样直接赋值的方式是浅拷贝,对象a的内存只存在一份,相当于b指向了a对象的内存区域。而深拷贝,使用对象的拷贝方法,完全复制了一份a对象,此时内存空间多了一份与a对象内存内容完全一样的内存空间,b对象指向它。
浅拷贝
上图是浅拷贝后的内存图。
深拷贝
上图是深拷贝后的内存图。
  在我看来,所谓的浅拷贝,其实在C语言里只是指针的赋值没有任何含义,在参考了glib的数据结构代码后,在它的源代码中,它的观点与java的并不同。它观点是如果对象A持有对象B,当我们需要拷贝A时,浅拷贝时,只拷贝属于A对象的内存,而不拷贝对象B的内存,只保留它的指针(引用)。而深拷贝,就是即拷贝A对象的内存也拷贝B对象的内存,和java相似。后文中我们所说的深或浅概率,都是指这个意思,而不是java的概率。

接口设计

设计原则

我们遵循以下三个原则:
	1. 接口完备,可以适用于各种使用场景。
	2. 接口数量最小化,让使用者感觉简单易用。
	3. 在接口数量最小化的基础上,接口要鲜明的体现当前数据结构的特点。

接下来我会一一介绍我设计的接口,并描述我为什么要这样设计。
我先提一句,我设计的通用链表是采用面向对象的编程思维设计的,我们把链表看作一个类,实例化的链表看作链表对象,
也就类似java中的数据结构的类。

链表的创建与销毁

  对象嘛,new和free方法必不可缺。
(1)new方法的作用 : 为对象分配内存,并进行初始化。
(2)free方法的作用 :释放对象的内存。

notes :
  在一些软件设计中,常常默认new方法为分配内存,但并不进行初始化,他们采用new方法分配对象内存,init方法来对象进行初始化,当需要分配内存又要初始化时,使用create方法,这种命名也不错,但我们采用这里采用java的思想,new方法会调用类的构造方法进行初始化。即我们的new方法即会分配内存也会进行初始化。

创建方法的函数原型如下:

// Slist new
Slist *slist_new(void); // 创建单向链表对象
// 创建一个链表对象,用户根据自己的需求传入需要的回调函数
Slist *slist_new_full(SlistDataCmp *data_cmp,
                      SlistDataEqu *data_equ, 
                      SlistDataCopy *data_copy, 
                      SlistDataFree *data_free);

这其中有很多形参需要解释,部分形参的类型如下:

typedef int  SlistDataCmp(void *data1, void *data2); // 元素的大小比较
typedef int  SlistDataEqu(void *data1, void *data2); // 元素的是否相等
typedef void* SlistDataCopy(void *data); // 复制一个元素
typedef void SlistDataFree(void *data); // 释放一个元素

  可以看到我们设计了很多回调函数,借鉴了很多java的设计,glib的设计等,如cmp,equ方法等,但我们不是照搬,C语言也不是java,它们有很多不同特性,但是java的面向对象的设计确实值得借鉴学习,后面我们会使用这些回调方法,我们会一一介绍为什么需要这些回调函数,以及如何使用。

销毁方法的函数原型如下:

// Slist free
void slist_free(Slist *list);  // shallow free
void slist_free_deep(Slist *list);  // deep free

  浅释放方法不会释放存储在链表中数据元素,而深释放会释放数据元素,而深释放需要调用释放元素的回调函数。即SlistDataCopy函数。
设计要点:
  深和浅的概率来自于软件设计中类和类之间的关系,当一个对象A持有另一个或多个对象B时,对象A和对象B之间的关系可以用两个概率来描述,即聚合和组合。
  所谓聚合关系,A和B是相对独立的,A消失时,B并不会消失,它们可以独立独立存在,如人和车子,当车子被释放了,人仍然存在,人不在了,车上仍然可以存在,它们之间的联系相对组合关系,没有那么联系紧密。
  所谓组合关系,顾名思义,A是由B组成的,B是A的组成成分,它们联系紧密,如车子和轮子,车子不存在了,轮子还有什么意义呢?
  当然是否是组合关系需要结合具体的应用场景来判断,A不存在了,B可能存在在其他的地方,这种情况,我们就可以说A和B是一种聚合关系,A和B必须同时存在,它们就是一种组合关系。
总结
  总结一下,当你判断A对象需要持有B对象,并且你打算使用此链表让A对象存入B对象,考虑释放A对象时,当A和B是组合关系时,使用deep free,如果是聚合关系时,使用shallow free。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值