前言
再好的编程技巧,也无法让一个笨拙的算法起死回生。选择错误的算法,变注定了失败的命令。回归于主题,如果将一个叙述完整的算法转化成程序,是经过积累才能写得出来的。那么,如何设计一个算法,使它适用于任何数据结构呢?
什么叫泛化
我们也许会想,如何在即将处理的未知数据结构上,正确的实现所有的操作呢?只要把操作对象的类型加以抽象化,把操作对象的标示法和区间目标的移动行为抽象化,整个算法也就在一个抽象的层面工作了,整个过程称为算法的泛型化,简称泛化。
算法泛化实例
让我们看看算法泛化的一个实例,以简单的循序查找为例,假设我们要编写一个find()函数,在array中查找特地值,面对整数array,我们的直觉是:
int *find(int *arr,int size,int value)
{
int i = 0;
for (i = 0; i < size; i++)
if (arr[i] == value)
break;
return &(arr[i]);
}
该函数在某个区间内查找value。返回的是一个指针,指向它所找到的第一个符合条件的元素;如果没有找到,就返回最后的一个元素的下一个位置。
通过一个实例来验证是否能查找到元素。下面是find()函数的使用
#include <iostream>
using namespace std;
int *find(int *arr,int size,int value)
{
int i = 0;
for (i = 0; i < size; i++)
if (arr[i] == value)
break;
return &(arr[i]);
}
int main(int argc, char const *argv[])
{
const int SIZE = 10;
int arr[SIZE] = {0,1,2,3,4,5,6,7,8,9};
int len = sizeof(arr) / sizeof(int);
int *end = arr + SIZE;
int value = 6; //查找的值
int *res = find(arr,len,value);
if (res == end)
cout <<value<< " not found"<<endl;
else
cout <<value<< " found"<<endl;
return 0;
}
输出结果:
find()函数的这种做法暴露太多的实现细节,也因此太过依附特定的容器。为了让find()适用所有的类型的容器,其操作应该抽象化些,让find()接受两个指针类型的参数,标出一个操作区间,改进上面的find()函数的写法:
int *find(int *begin, int *end, int value)
{
while (begin != end && *begin != value)
++begin;
return begin;
}
上面的find()函数在一个“前闭后开”区间[begin,end]内查找value,并返回一个指针,指向它所找到的第一个符合条件的元素:如果没有找到,就返回end。下面是使用find()函数的例子:
#include <iostream>
using namespace std;
int *find(int *begin, int *end, int value)
{
while (begin != end && *begin != value)
++begin;
return begin;
}
int main(int argc, char const *argv[])
{
const int SIZE = 10;
int arr[SIZE] = {0,1,2,3,4,5,6,7,8,9};
int *end = arr + SIZE;
int value = 6; //查找的值
int *res = find(arr,end,value);
if (res == end)
cout <<value<< " not found"<<endl;
else
cout <<value<< " found"<<endl;
return 0;
}
输出结果:
find()函数也可以方便地用来查找array的子区间:
int *res = find(arr,end,value);
模板与泛型编程
面向对象的编程OOP和泛型编程都能处理编程程序时不知道类型的情况。不同之处在于,OOP编程能处理类型在程序运行之前的未知的情况;而在泛型编程中,在编译时就能获知类型了。
容器、迭代器和算法都是泛型编程的例子,当我们编写一个泛型程序find()时,是独立于特定类型来编写代码的,当使用一个泛型程序时,我们提供类型或值,程序实例可在其上运行。
模板是泛型编程的基础。我们不必了解模板是如何定义的就使用它们。接下来看看find()的模板实现:
template<typename T>
T *find(T *begin, T *end, T& value)
{
while (begin != end && *begin != value)
++begin;
return begin;
}
这样的find()很好,几乎适用于任何容器,只要该容器允许指针入,而指针们又支持以下四种find()函数中出现的操作行为:
- != 运算符
- *取值运算符
- 递增运算符(++)
- 复制,一边返回函数的返回值
模板与泛型编程find()例子如下:
#include <iostream>
using namespace std;
template<typename T>
T *find(T *begin, T *end, T& value)
{
while (begin != end && *begin != value)
++begin;
return begin;
}
int main(int argc, char const *argv[])
{
const int SIZE = 10;
int arr[SIZE] = {0,1,2,3,4,5,6,7,8,9};
int *end = arr + SIZE;
int value = 6; //查找的值
int *res = find(arr,end,value);
if (res == end)
cout <<value<< " not found"<<endl;
else
cout <<value<< " found"<<endl;
return 0;
}
输出结果:
find()函数的迭代器实现
迭代器是一种行为类似的指针对象,换句话说,是一种smart pointers。现在我将find()函数内的指针以迭代器取代,重新写:
#include <iostream>
using namespace std;
template<class Iterator, class T>
Iterator find(Iterator begin, Iterator end, T& value)
{
while (begin != end && *begin != value)
++begin;
return begin;
}
int main(int argc, char const *argv[])
{
const int SIZE = 10;
int arr[SIZE] = {0,1,2,3,4,5,6,7,8,9};
int *end = arr + SIZE;
int value = 6; //查找的值
int *res = find(arr,end,value);
if (res == end)
cout <<value<< " not found"<<endl;
else
cout <<value<< " found"<<endl;
return 0;
}
输出结果:
到这里,这便是一个完全的泛型化find()函数。你可以在任何C++标准库的某个头文件里看到它。长相几乎一模一样。
扫二维码关注微信公众号,获取技术干货
参考:STL源码剖析