C++初学者指南 第十二篇(2)

来源:互联网 发布:大数据英语翻译 编辑:程序博客网 时间:2024/05/20 13:16
转载请标明出处:http://blog.csdn.net/zhangxingping

模板是C++中最高级和最强大的特性之一。模板并不是在最初的关于C++的说明中就有的,而是后来才被加入到C++中的。如今所有的C++编译器都是支持模板特性的。模板能够帮助我们对编程中最难以琢磨的东西进行归档:增加代码的复用性。

通过使用模板,我们可以创建通用的函数和类。在这些通用的函数和类中,其要操作的数据的类型是作为参数被传入的。因此,我们可以针对多种类型的数据只编写一次代码,一个函数或者类;而不用针对不同数据类型编写不同版本的代码。下面我们就将学习通用函数和类。

 

基本技能12.2:通用函数

通用的函数定义了针对不同数据类型的一套通用的操作,其操作对象的数据类型是作为一个参数被传入的。通过使用一个通用的函数,一个简单的通用的过程就可以被应用到不同的数据类型上去。或许我们都知道,许多算法在逻辑上都是通用的,而不管算法所操作的数据类型是否相同。例如,快速排序算法就是这样,不管我们是针对一组整形数据或者是针对一组浮点型数据进行排序。区别只在于被排序对象的类型不一样。通过定义通用函数,我们实际上定义的是算法的本质,使得算法独立于数据类型。一旦我们完成了通用函数的定义,编译器进行编译时会根据调用该函数时候的实际数据类型自动生成正确的代码。从本质上来说,一旦我们创建了一个通用的函数,我们就等于创建了能自动对自己进行重载的函数。

通用函数是通过关键字template模板来创建的。模板这个词恰如其分的表达了他在C++中的作用:被用来创建描述要做什么的函数样板,而由编译器在必要的时候补充细节。定义通用函数的一般形式为:

template <class Ttype> 返回值类型 函数名称(参数列表)

{

    函数体

}

其中的Ttype是数据类型的占位符。这个类型将被用来定义函数操作中声明的数据的类型。编译器在生成特定函数版本的时候会自动地使用实际的数据类型来替换Ttype。虽然我们在上述形式中使用的是class关键字来指定通用函数声明中的通用类型,我们也是可以使用typename 这个关键字的。

下面的实例中,我们创建了一个通用的用于交换两个变量值的函数。由于交换两个值的过程和对应的变量的类型没有关系,所以把这个过程编写成通过函数是非常明智的选择。

//函数模板示例#include <iostream>using namespace std; template<class X> void swapargs(X &a, X&b){    X temp;    temp = a;    a = b;    b = temp;} int main(){    int i = 10, j = 20;    float x = 10.1f, y = 23.2f;    char a = 'x', b = 'z';     cout <<"Original i,j:" << i <<' ' << j << '\n';    cout <<"Original x,y:" << x <<' ' << y << '\n';    cout <<"Original a,b:" << a <<' ' << b << '\n';     //编译器会自动根据传入参数的类型创建不同    //版本的swapargs()函数    swapargs(i, j);    swapargs(x, y);    swapargs(a, b);     cout <<"Swapped i,j:" << i <<' ' << j << '\n';    cout <<"Swapped x,y:" << x <<' ' << y << '\n';    cout <<"Swapped a,b:" << a <<' ' << b << '\n';     return 0;}

我们将对上面的程序进行仔细的研究。首先是以第一行:

template<class X> void swapargs(X &a, X&b)

这句告诉编译器两件事情:即将创建模板并且是通用函数类型的模板。其中X就是作为占位符的通用数据类型,也就是交换的数据的类型。在main()函数中,我们在调用函数swapargs()函数的时候分别三次传入了不同的数据类型的变量作为参数:整型,单精度的浮点数以及字符类型。由于swapargs()函数是通用函数,编译器就自动地针对上述三种类型创建不同版本的函数,分别用来交换整型变量的值,单精度浮点型变量的值以及字符类型变量的值。这样一来同一个通用的交换函数就可以被用来针对不同数据类型的数据进行交换操作。

这里涉及到了和模板相关的几个重要的术语。首先,通用函数又叫做模板函数。本书中将交替使用这两个相同含义的术语。当编译器创建了通用函数的某一个特定的版本函数的时候,我们就说通用函数被特定化了,也被叫做是由模板函数生成特定的函数。这种生成的过程被称为实例化。换句话说,由模板函数生成的函数实际上是模板函数的实例化。

有两个通用数据类型的函数

在使用模板函数的时候我们还可以指定两个或者更多的通用数据类型。他们之间用逗号间隔。例如,下面的程序就创建了有两个通用数据类型的函数:

#include <iostream>using namespace std; //模板函数需要两个通用的数据类型template <class Type1, class Type2>void myFunc(Type1 x, Type2 y){       cout << x << ' ' << y << "\n";} int main(){    myFunc(10, "hi");    myFunc(0.23, 10L);        return 0;}

        在上面的程序中,有两个数据类型占位符Type1Type2。在进行编译的时候需要生成模板函数myFunc()的特定实例,编译器分别用整形、字符指针和双精度浮点型、长整形替换模板中的数据类型占位符。

显式重载通用函数

我们在前面提到过,模板函数会在需要的时候自动地被重载。这里需要指出的是我们也是可以显式地对其进行重载。这个过程通常被称为显式地实例化。如果我们对一个通用函数进行了重载,那么重载的函数将覆盖模板函数对应的数据类型的版本。例如下面的程序。我们重写编写了前面的用于交换两个参数值得示例程序:

//显式地生成模板函数的实例#include <iostream>using namespace std; template <class X>    void swapargs(X &a, X &b){    X temp;    temp =a;    a = b;    cout << "Inside template swapargs.\n"; } //下面的这个函数就覆盖了模板函数针对整形类型的实例void swapargs(int &a,int &b){    int temp;    temp = a;    a = b;    b = temp;    cout << "Inside swapargs int specialization./n";} int main(){    int i = 10, j = 20;    float x = 10.1f, y = 23.3f;    char a = 'x', b = 'z';     cout << "Original i, j:" << i << ' ' << j << '\n';    cout << "Original x, y:" << x << ' ' << y << '\n';    cout << "Original a, b:" << a << ' ' << b << '\n';     swapargs(i,j); //调用的是显式地指定了参数类型的函数    swapargs(x,y); //调用模板函数    swapargs(a,b); //调用模板函数     cout << "Swapped i, j:" << i << ' ' << j << '\n';    cout << "Swapped x, y:" << x << ' ' << y << '\n';    cout << "Swapped a, b:" << a << ' ' << b << '\n';     return 0;}

上面程序的输出结果如下:

Original i, j:10 20

Original x, y:10.1 23.3

Original a, b:x z

Inside swapargs int specialization.

Inside template swapargs.

Inside template swapargs.

Swapped i, j:20 10

Swapped x, y:23.3 23.3

Swapped a, b:z z

正如程序中注释描述的那样,swapargs(i, j);调用的是显式地指定了参数类型为int的这个函数。这个函数覆盖了模板函数swapargs()模板函数针对int类型的实例化函数。

后来,人们引入了另外一种语法来描述这种显示实例化的函数。这种新的语法中使用到template关键字。例如,上述程序中针对int类型的显示实例化就可用下面的程序完成:

template<> void swapargs<int>(int &a,int &b){    int temp;    temp = a;    a = b;    b = temp;    cout << "Inside swapargs int specialization.\n";}

从中我们可以看出,这种新的语法中使用template<>来描述要对模板函数进行实例化。具体实例化的数据类型被放在了函数名称后面的尖括号中。这种形式可用来针对任何模板函数的任何类型进行实例化。两种描述方式没有孰优孰劣之分。长期来看,后一种方式也许是一种更好的方式。

显示地对模板函数进行实例化似的我们可以针对特殊的情况进行处理。一般来说,如果针对不同的数据类型我们需要使用不同的函数实现,此时最好是使用函数重载还不是模板函数。