众所周知,C++的STL实现了很多的算法,这些算法给程序员带来了很多的方便,一般情况下,如果你的项目没有特殊的要求,采用这些算法都比我们自己动手起来要简洁、高效。这些算法的成功,依赖的是大家耳熟能详的泛型思想。
众所周知,C++的STL实现了很多的算法,这些算法给程序员带来了很多的方便,一般情况下,如果你的项目没有特殊的要求,采用这些算法都比我们自己动手起来要简洁、高效。这些算法的成功,依赖的是大家耳熟能详的泛型思想。 你可能会说 “泛型编程”我很熟了,不就写几个模板?没错,泛型编程是应用模板技术做一些数据抽象的过程。但在享受着它给你带来便利的同时,你是否想过这背后的设计需求是什么吗?你是否想过为什么要这样去实现吗?可能你觉得不必要去深究这些东西,毕竟只要会如何使用它就好了。但是如果你知道了为何要这样设计的原因后,我想对你总是会有些好处的。
让我们看看一个List数据结构的实现,这里我并不是去剖析STL中list的实现代码。而是用C去实现类似的功能,我想暂时先让你“痛苦”一下,坐拖拉机走山路。然后再让你坐豪华汽车跑高速。
Code
typedef struct _List
{
long Count ; //链表中的元素总数
void **Item ; //链表中的元素集合
long (*Add)(struct _List *pList , void *pValue) ;
long (*Clear)(struct _List *pList) ;
Bool (*Contains)(struct _List *pList , const void *pValue) ;
long (*IndexOf)(struct _List *pList , const void *pValue) ;
long (*Insert)(struct _List *pList , long index , void *pValue) ;
long (*Remove)(struct _List *pList , const void *pValue) ;
long (*RemoveAt)(struct _List *pList , long index) ;
long (*Sort)(struct _List *pList) ;
long (*Destroy)(struct _List *pList) ;
long (*Equals)(const void *pValue1 , const void *pValue2) ;
} List;
这个定义模拟了面向对象中的数据与方法整合在一起,它实现存放任何类型指针的集合以及对集合上的操作。
首先我们看一下这样的定义有没有什么问题,注意看结构中出现了一个Sort方法,看起来需要实现对整个 List的数据进行排序。这里就涉及到一个问题,大家都知道排序有很多种类,比如:快速排序,冒泡排序,多关键字排序等。这些算法也各有特点,每个应用场景都不太一样。而现在把 Sort 功能与数据封装在一起,让程序库使用者如何选择想要的排序算法呢?针对每一种算法提供一个实现,封装进数据结构中,这样的话必须针对每一种数据类型都提供算法的实现,但是这样的话复用性不好,而且会产生很多重复代码。
为了不把问题搞的太复杂,也许你认为对算法的选择不那么重要,所以就选择任意一个能达到目的的算法,比如选择冒泡排序来实现,见下面的代码:
Code
long List_Sort(List *pList)
{
long ret = SUCCEED ;
long i = 0 ;
long j = 0 ;
void *pTemp = NULL ;
if (NULL == pList)
{
ret = GERR_POINT_IS_NULL ;
return ret ;
}
for (i = 0 ; i < pList->Count - 1 ; i++)
{
for (j = i + 1 ; j < pList->Count ; j++)
{
if (pList->Equals(pList->Item[i] , pList->Item[j]) > 0) // 用了Equals方法
{
pTemp = pList->Item[i] ;
pList->Item[i] = pList->Item[j] ;
pList->Item[j] = pTemp ;
}
}
}
return ret ;
}
请注意有注释的那段代码,Sort方法调用了一个叫 Equals方法。该方法也是由集合封装的。粗看起来没有什么问题,接着我们再看看Equals 方法的实现。
long ArrayList_Equals(const void *pValue1 , const void *pValue2)
{
return strcmp(pValue1 , pValue2) ;
}
啊,看起来真的好简洁,直接调用了一个库函数 strcmp就完成了任务。我想学过C的程序员一看到 strcmp就很亲切。不过不要高兴的太早,让我们回到数据项的定义:void **Item ;这里的指针类型是 void,说明它可以存放任何数据类型。而刚才Equals 的实现却是用字符串的比较函数,也就是说这个list 只能存放字符类型的才能保证产生正确的结果。要是遇上里面存放的是INT或其它结构体类型的数据,那不就麻烦了。难道需要针对每一种数据类型都实现一个排序版本?按这样写出来的代码会非常臃肿,并且难于维护和使用。
如果你的使用的语言支持泛型编程,那么这些问题就不用我们操心了。让我们回去看看在应用STL排序算法的时候是如何做到解决上面提到的两个问题的。
看一段 MSDN 的示例代码:
Code
bool UDgreater ( int elem1, int elem2 )
{
return elem1 > elem2;
}
int main( )
{
using namespace std;
vector <int> v1;
sort( v1.begin( ), v1.end( ) );
sort( v1.begin( ), v1.end( ), UDgreater );
}
这里没有列出STL的源码,但从sort方法的调用可以看出,STL 中把算法独立出来了。不局限于某种数据类型, 这样就解决了僵硬的缺点,让程序员可以自由的选择实际需要的算法。当然这些都依赖于编译器的支持,如果用C实现这样的功能,恐怕需要费一番周折了。
接下来 sort( v1.begin( ), v1.end( ), UDgreater ); 看第三个参数,直接传了个函数指针。没有把功能做僵硬,而把“比较运算子”交由最终使用者去实现了,这样就达到了可扩展性、可伸缩性的目的。
相关文章阅读:
用C语言封装数据与方法