深入浅出理解:函数模板与类模板、存在的实际意义以及使用方法,

时间:2022-12-09 15:57:48


在讲模板之前,先闲扯一下吧:C++最重要的特性之一就是,代码的重用,为了实现代码重用,代码就必须具有通用性

通用代码不受数据类型的影响,并且可以自动适应数据类型的变化,这种程序设计类型称为参数化程序设计。

模板是C++支持参数化程序设计的工具,通过它可以实现参数化多态性。

所谓参数化多态性是指:将程序所处理的对象的类型参数化,使得一段程序可以处理多种不同类型的对象。

简单的理解,模板就是为了让代码的通用性更强。有了以上的理解, 下面理解函数模板和类模板就轻松多了。

(2)函数模板

要讲函数模板,就得先讲一讲函数重载,相信大家都知道,函数重载通常是对于不同的数据类型完成类似的操作。

在很多时候,一个算法其实是可以处理多种数据类型的。但是用函数实现算法时,即使设计为重载函数也只是

使用相同的函数名,函数体仍然要分别定义。下面举一个求绝对值的函数重载的实例,方便大家理解。

int abs(int x)

{

return x<0?-x:x;

}

double abs(double x)

{

return x<0?-x:x;

}

可以看出,这两个函数的功能和函数体是一模一样的,但是为了实现对不同数据类型的数据求绝对值操作,我们不得不写两个

同名函数来实现操作,如果能写一段通用代码适用与多种不同数据类型该多好呢,这会使代码的可重用性大大提高,从而提高

软件的开发效率。所以聪明的人们便设计出了这么一个工具,那就是函数模板,程序员只需对函数模板编写一次,然后基于调

用函数时提供的参数类型,C++编译器将自动产生相应的函数来正确处理该类型的数据。

函数模板的定义形式如下:

template<模板参数列表>

类型名  函数名(参数列表)

{

函数体的定义;

}

说明:class或者typename标识符,指明可以接收一个类型参数,这些类型参数代表的是类型,可以是系统预定义的数据类型,也

可以是自定义类型。类型参数可以用来指定函数模板本身的形参类型、返回值类型,以及声明函数中的局部变量。

下面举一个求绝对值的函数模板的程序。

#include<iostream>

using namespace std;

template<typename T>

T  abs(T x)

{

return x<0?-x:x;

}

int  main()

{

int n_number=-5;

double d_number=-5.5;

cout<<abs(n_number)<<ebdl;

cout<<abs(d_number)<<endl;

return 0;

}

运行结果如下:

深入浅出理解:函数模板与类模板、存在的实际意义以及使用方法,

在main函数中两次调用abs()函数,

对于调用表达式abs(n_number)时,由于n_number为int类型,所以推导出模板中类型参数T为int;

当调用表达式abs(d_number)时,由于d_number为double类型,所以推导出模板类型参数T为double;

当类型参数的含义确定后,编译器将以函数模板作为一个样板,生成一个函数,这一过程便是传说中的函数模板实例化;

其实编译器会实例化以下两个函数:

int abs(int x)

{

return x<0?-x:x;

}

double  abs(double x)

{

return x<0?-x:x;

}

下面来一个稍微复杂一点的函数模板的例子,供各位深入理解一下函数模板的用法。

#include<iostream>

using namespace  std;

template<class T>

void outputArray(const T *array,int count)

{

for(int i=0;i<count;i++)

{

cout<<array[i]<<" ";

}

cout<<endl;

}

int main()

{

const  int  a_count=8,b_count=8,c_count=20;

int    a[a_count]={1,2,3,4,5,6,7,8};

double    b[b_count]={1.1,2.2,3.3,4.4,5.5,6.6,7.7,8.8};

char   c[c_count]="Welcome to see you!";

cout<<"数组a:"<<endl;

outputArray(a,a_count);

cout<<"数组b:"<<endl;

outputArray(b,b_count);

cout<<"数组c:"<<endl;

outputArray(c,c_count);

return 0;

}

运行效果如下图:

深入浅出理解:函数模板与类模板、存在的实际意义以及使用方法,

(3)类模板

类模板可以使用户为类定义一种模式,使得类中的某些数据成员、成员函数、返回值或者局部

变量能取任意类型。(包括系统预定义和用户自定义的)

如果说类是对一组对象公共性质的抽象,而类模板则是对不同类的公共性质的抽象,所以说类模板是更高层次的抽象。

类模板声明的语法形式:

template<模板参数表>

class  类名

{

类成员声明;

}

其中类成员的声明方法与普通类的声明方法其实是一模一样的,只是它的各个成员(数据成员和函数成员)通常要用到模板的类型参数T;

注意:如果要在类模板以外定义成员函数,要采用以下形式

template <模板参数表>

类型名  类名<模板参数标识符列表>::函数名(参数表)

一个类模板声明,它自身并不是一个类,它说明类的一个家族,只有当它被其它代码引用时,模板才根据需要生成具体的类。

使用一个模板类来建立对象时,格式如下:

模板名<模板参数表>对象名1,....对象名N;

下面举个例子,方便大家理解,

本例中,声明一个实现任意类型数据存取的类模板Store,然后通过具体数据类型参数对类模板进行实例化,

生成类,然后类再被实例化,

生成对象s1,s2,s3,d.

源程序如下:

 #include<iostream>
#include<cstdlib>
using namespace std;

struct Student
{
int id;
float gpa;
};
template<class T>
class Store
{
private:
T item;
bool haveValue;
public:
Store();
T& getElem();
void putElem(const T &x);
};
template<class T>
Store<T>::Store():haveValue(false){}

template<class T>
T& Store<T>::getElem()
{
if(!haveValue)
{
cout<<"没有数据"<<endl;
exit(1);
}
return item;
}

template<class T>
void Store<T>::putElem(const T &x)
{
haveValue=true;
item=x;
}

int main()
{
Store<int> s1,s2;
s1.putElem(3);
s2.putElem(-7);
cout<<s1.getElem()<<" "<<s2.getElem()<<endl;

Student g={101011,99};
Store<Student>s3;
s3.putElem(g);
cout<<"The student ID is: "<<s3.getElem().id<<endl;

Store<double>d;
cout<<d.getElem()<<endl;

return 0;
}

运行结果:


深入浅出理解:函数模板与类模板、存在的实际意义以及使用方法,