C++算法学习——预备知识(2)——函数模板

来源:互联网 发布:linux 总线 设备 驱动 编辑:程序博客网 时间:2024/04/29 20:44

函数模板

函数模板是可以使用通用类型运行的特殊功能。这允许我们创建一个函数模板,其功能可以适应多种类型或类,而不会重复每种类型的整个代码。
在C ++中,这可以使用模板参数来实现。模板参数是一种特殊类型的参数,可用于将类型作为参数传递:就像常规函数参数可用于将值传递给函数一样,模板参数也允许将类型传递给函数。这些功能模板可以使用这些参数,就像它们是任何其他常规类型一样。
两种使用类型参数声明函数模板的格式为:

template <class identifier> function_declaration; //传递类型为类template <typename identifier> function_declaration;//传递类型为某种类型

两种原型之间的唯一区别是使用关键字class或关键字typename。 它的用法是模糊的,因为这两个表达式具有完全相同的意义,并且行为完全一样。
例如,创建一个模板函数,可以比较两个对象中较大的一个。我们可以这样:

template <class myType>myType GetMax (myType a, myType b) { return (a>b?a:b);}

在这里,我们创建了一个模板函数,以myType为模板参数。此模板参数表示尚未指定的类型,但可在模板函数中使用,就像它是常规类型一样。你可以看到,函数模板GetMax返回这个仍然未定义类型的两个参数中较大的一个。
例如,要调用GetMax来比较int类型的两个整数值,我们可以写:

int x,y;GetMax <int> (x,y)

当编译器遇到对模板函数的这个调用时,它使用模板自动生成一个函数来替换myType的每个外观,通过传递给实际的模板参数(在这种情况下为int),然后调用它。该过程由编译器自动执行,程序员不可见。
我们再举一个例子,创建一个模板函数,交换两个数(具体类型未知),我们可以这样:

template<typename T>void Swap(T & a, T & b) {   T temp = b;   b = a;   a = temp;}

运行示例

让我们写个程序运行一下(看看延续我们在上一个系列的代码风格,先声明再定义会出现什么):

#include <iostream>#include <string>using namespace std;/*模板声明*/template <class T> getMax(T x, T y);template <typename T> swap(T &a, T &b);int main(){    int i = 5, k = 9;    double m = 8.7, n =4.3;    double q = getMax<double> (m, n);    int p = getMax<int>(i, k);    string str1,str2;    str1 = "redAnt"; str2 = "GDMU";    swap<string>(str1, str2);    cout << q << endl;    cout << p << endl;    cout << str1 << "," << str2;}/*模板定义*/template <class T>T getMax(T x, T y){    T result;    result = (x > y) ? x : y;    return result;}template <typename T>void Swap(T & a, T & b) {   T temp = b;   b = a;   a = temp;}

嗯,很标准的一段代码对不对?先声明再定义,又没有语法错误。好,我们运行一下看看:

哇,怎么那么多错误??不急,一个个留意。看到第四行,main函数中的getMax没有定义。我们明明已经在这之前声明了啊。
这里注意了,这只能说明一个原因,那就是我们定义的方法根本就没有执行,也就是说,执行Main函数的时候,遇到这个getMax函数它是不会去找到相应的定义。template的声明定义是不会预编译的,我们把实现的代码放前面就好了。

#include <iostream>#include <string>using namespace std;/* *这里我们 T getMax(T x, T y),因为返回的类型也是T的相应类型 *如果不好理解可以把T 想象成我们熟悉的 int double 等类型 */template <class T>T getMax(T x, T y){    T result;    result = (x > y) ? x : y;    return result;}/*这里执行的是一个操作,没有返回值,我们用void类型就可以了*/template <typename T>void Swap(T & a, T & b) {   T temp = b;   b = a;   a = temp;}int main(){    int i = 5, k = 9;    double m = 8.7, n =4.3;    double q = getMax<double> (m, n);    int p = getMax<int>(i, k);    string str1,str2;    str1 = "redAnt"; str2 = "GDMU";    swap<string>(str1, str2);    cout << q << endl;    cout << p << endl;    cout << str1 << "," << str2;}

再次运行:

可以了。

代码分析

在上面的例子中,我们使用了两个函数模板GetMax()。 第一次使用类型为int的参数,第二个参数的类型为long。 编译器已经实例化,然后每次调用相应的版本的函数。你可以看到,类型T在GetMax()模板函数中使用,甚至可以声明该类型的新对象:

T result;

因此,当函数模板被实例化为特定类型时,结果将是与参数a和b相同类型的对象。
在通用类型T用作GetMax的参数的特定情况下,编译器可以自动找出哪个数据类型必须实例化,而无需在尖括号中明确指定它(就像我们在指定和)。 所以我们可以写:

int i,j;getMax (i,j);

由于i和j都是int类型,编译器可以自动发现模板参数只能是int。 这种隐式方法产生完全相同的结果.
但是如果我们这样指定呢?

int i;long l;k = getMax (i,l);

请注意,在这种情况下,我们调用了我们的函数模板GetMax(),而不显式指定尖括号<>之间的类型。 编译器自动确定每个调用需要什么类型。
因为我们的模板函数只包含一个模板参数(T类),而且函数模板本身接受两个参数,这两个T类型,我们不能用不同类型的两个对象作为参数调用我们的函数模板。
这是不正确的,因为我们的GetMax函数模板需要两个相同类型的参数,在这个调用中,我们使用两种不同类型的对象。
但是我们还可以通过定义接受多个类型参数的函数模板来解决这个问题,只需在尖括号之间指定更多的模板参数即可。 例如:

template <class T, class U>T getMax (T a, U b) {  return (a>b?a:b);}

在这种情况下,我们的函数模板getMax()接受不同类型的两个参数,并返回与传递的第一个参数(T)相同类型的对象(即你的函数的返回类型)。 例如,在该声明之后,我们可以调用getMax():

int i,j;long l;i = getMax<int,long> (j,l);

或者更简单的

i = getMax (j,l);

即使j和l有不同的类型,因为编译器可以确定适当的实例化.这样做我们就可以实现任意组合,而不用写出各种类型的对应函数。极大的减少了代码量!对于两种使用方法,我个人更喜欢第一种,因为我们在使用的过程中表明我们用的类型,会使得程序更加易读,不易出错,就像我们惯用的vector一样。

思考

我们刚刚说了,template的声明定义是不会预编译的。所以我们都要把实现的代码放在Main函数之前,可是我们在大型的程序中,使用到类,我们都是分文件放的,比如我们会 .h文件放的是声明 .cpp放的是定义,Main函数也是cpp,这样的话我们岂不是所有的文件都放在Main函数的文件上?那个cpp不是特别庞大?然而事实上我们并没有这样做。我们肯定有办法避免这样的情况发生。至于怎么解决,等下一篇讲完类模板再提出来。

阅读全文
0 0
原创粉丝点击