泛型程序设计是程序设计语言的一种风格或范式。允许程序员在编写代码时使用一些以后才指定的类型,在实例化时(instantiate)作为参数指明这些类型。
泛型的定义主要有以下两种:
- 在程序编码中一些包含类型参数的类型,也就是说泛型的参数只可以代表类,不能代表个别对象。(这是当今较常见的定义)
在程序编码中一些包含参数的类。其参数可以代表类或对象等等。(现在人们大多把这称作模板)
不论使用那个定义,泛型的参数在真正使用泛型时都必须作出指明。
(http://zh.wikipedia.org/wiki/%E6%B3%9B%E5%9E%8B)对于C++来说,泛型很重的一个应用就是STL(标准模板程序库),STL可以让你重复使用既有的算法,而不必在类似情况下重新编写代码,这就是泛型算法的魅力,它不与特定的数据结构或对象类型绑定在一起,有很高的效率。
让我看一个具体的例子,考虑下如何将普通C版本的线性查找变为泛型算法?
如标准库中的strchr函数:
char* strchr(char* s, char c);
一个简易的实现代码如下:
char* strchr(char* s, char c)
{
while(*s!= ’\0’ && *s != c)
++s;
return *s == c ? s : (char *)0;
}
考虑到一般化,可用一个指针last表示结束,则上述代码改为:
char* strchr(char* first, char* last, charc)
{
while(first!= last && *s != c)
++first;
return first;
}
对于char A[n],查找’a’; 可用char* result = strchr(A,A+n,’a’);
在C++我们可以用template将参数类型参数化,实现一个更加一般的方法:
这里用两种实现方式:
//Case1 :
template<class T>
T* find(T*first, T*last, const T& value);
//Case2:
template<class Iterator, class T>
Iteratorfind(Iterator first, Iterator last, const T& value)
{
while(first != last && *s != c)
++first;
return first;
}
显然:方法2更具一般性,它不要求参数first和last类型必须为指针;
那么对于这个更具一般性的方法,要求它具备那些能力呢?
从上面代码中可以看出,需要:
1) 比较能力,判断是否到达结尾(first != last)
2) 提取能力(*s)
3) 移动到下一个元素(++first)
让我来看一个特殊的数据类型:链表;
template<class T>
struct node {
T value;
node* next;
};
那么我们如何通过上述泛型算法查找一个特定的节点呢,也就是如何实现上面的三种能力?
首先考虑能否将Iterator 设置为 Node*,对于++操作,我们是希望移动到下一个元素,但是Node* 的++操作有既定的语法意义,无法进行操作符重载实现;
我们可以写一个简单的外覆类,让它看上去像是Node*,但是可以对++进行重载;
template<class T>
struct node_wapper{
Node* node;
node_wapper(Node* n):node(n){};
T &operator*(){return node->value;}
node_wapper& operator++(){
node = node->next;
return *this;
} //前置加
bool operator!=(const node_wapper&i){
rerurn node != i.node;
}
};
其函数调用可写为:
find(node_wapper(head),node_wapper(),value);