今天,和大家分享与模板相关的知识,本次内容主要为函数模板和类模板。

泛型编程

大家还记得怎么去实现交换函数吗?

我们以int类型作为例子,实现出一个交换函数。

让我们一起来认识一下C++的模板_类模板

我们来测试一下:

让我们一起来认识一下C++的模板_类模板_02

从运行结果看,交换函数的功能并没有什么问题。

好了,我们有了int类型的交换函数了。可是,现在我不想交换int类型的数据了,我想交换double类型的数据了,这个函数能实现吗?我们把变量a和变量b的类型换成double看看。

让我们一起来认识一下C++的模板_函数模板_03

很明显,编译器报错了。这时候,就有同学说啦。这int类型的交换函数怎么能交换double类型呢?我们需要实现一个double类型的交换函数才行。

让我们一起来认识一下C++的模板_实例化_04

确实,当我们重载double类型的交换函数后,我们就能实现double类型的数据交换了。可如果现在我不想交换int类型,也不想交换double类型,就想交换float类型的数据。那程序是不是又不行了。

让我们一起来认识一下C++的模板_实例化_05

这时,又有同学说了,我们可以再加一个float类型的交换函数呀!这样的方法可行吗?当然可行,但是每当我们进行原来没有对应交换函数的数据类型交换时,就需要新增一个交换函数重载。

不知道大家,有没有见过金属器件制造的过程。

让我们一起来认识一下C++的模板_函数模板_06

在金属工艺中,大多数金属制品都会有一个膜具,不管你想要什么材料的都行,只需要把材料加进膜具里就可以得到不同材质的铸件。

在C++里也有这样类似的膜具,把不同的材料(类型)加进去,生成不同的铸件(即具体的代码)。那什么是泛型编程呢?泛型编程就是编写与类型无关的通用代码,它是一种代码复用的手段,以模板作为基础。

函数模板

模板包含有函数模板和类模板两个。

让我们一起来认识一下C++的模板_类模板_07

我们先来了解函数模板。

函数模板的定义

在定义函数模板前,我们需要定义模板参数。

模板参数的定义的定义方法是template关键字加尖括号,在尖括号中,用class或typename(这两个都可以用)加上模板参数的名字即可。如果你有多个模板参数的话,以逗号作为分割符。

下面,我们来演示一下:

让我们一起来认识一下C++的模板_函数模板_08

以上就是参数列表的定义方法。它是函数模板的基础,在此基础上加上函数的实现,一个函数模板就完成了。

编译器并不允许定义这么多的模板参数列表,我们把它先注释掉,然后,根据函数模板的格式,完成一个交换函数的定义

让我们一起来认识一下C++的模板_实例化_09

我们把之前实现的交换函数注释掉后,来测试一下:

让我们一起来认识一下C++的模板_类模板_10

从结果看,没什么问题,float类型的数据完成了交换,我们把float类型换成double类型再进行测试:

让我们一起来认识一下C++的模板_实例化_11

从结果看,没什么问题。

像上面Swap这样的函数,我们称之为模板函数。

模板函数的实现的原理,是让编译器通过传过来的参数类型来自动推导实例出相应的函数。

让我们一起来认识一下C++的模板_函数模板_12

然后,完成相应的交换功能。

我们将函数模板推导成对应类型的函数称之为函数模板实例化。

函数模板的实例化

函数模板的实例化又分为隐式实例化和显示实例化。由于交换函数比较特殊,我们以两个数相加的函数为例演示:

我们先来看看两个整型的相加:

让我们一起来认识一下C++的模板_函数模板_13

从程序结果来看,程序是能够正常运行并得出正确结果的。

接下来,我们把相加的数换成doule类型和int类型的,测试一下:

让我们一起来认识一下C++的模板_实例化_14

很显然,编译器报错了。这是为什么呢?这是因为函数模板无法自动的进行类型的转换,也就是它不知道模板参数该实例化成double类型还是int类型的函数。

那我们该怎么去解决这个问题呢?既然编译器不知道该实例化成什么类型,那我们可以告诉它不就好了吗?对于这一块的问题,我们可以用一下两种方式告诉编译器该实例化成什么类型。

第一种:对变量b进行强制类型转换。

让我们一起来认识一下C++的模板_函数模板_15

第二种:显示实例化,方式是在函数后面增加一个尖括号,尖括号里面填要实例化的类型。

让我们一起来认识一下C++的模板_实例化_16

前面我们学过函数是可以重载的,如果模板函数遇到了同名的函数,会怎么处理呢?

为了便于我们确认编译器到底使用那一个函数,我们做一些特殊的处理。

让我们一起来认识一下C++的模板_实例化_17

做完特殊处理后,我们来测试一下:

让我们一起来认识一下C++的模板_类模板_18

很明显,编译器并没有使用函数模板,而是使用了与函数模板同名的Add函数。这是为什么?对于与函数模板同名的函数,相当于函数模板的特化。图中的特化是对象是int整形,对于int类型这种情况,我们特殊对待走我们实现的函数,而不会走函数模板。

让我们一起来认识一下C++的模板_类模板_19

对于函数模板特化的作用,我们在后面的章节再细细讲述。

在学习数据结构的时候,相信大家都模拟过栈。

当我们模拟一个int类型的栈,是不是类似下面这样。

让我们一起来认识一下C++的模板_函数模板_20

我们这样实现的栈,可以对int类型进行操作,可当我们把数据的类型换成了double类型,我们的栈是不是就不能用了?这时候,有些同学就会说啦,我们可以再实现一个double类型的栈呀。

对,我们再实现一个double类型栈,问题就解决。可是类型不仅有自定义类型还有自定义类型,难道我们都要一一去实现它吗?

这显然是很麻烦的,那我们有没有什么方法去写一个通用的类呢?

当然有,我们把这样通用的类称之为类模板。

类模板

类模板的定义方式与函数模板的定义方式有一些类似,需要先定义出参数列表,然后,再完成相应的函数和成员。

让我们一起来认识一下C++的模板_函数模板_21

通过类模板的定义方式,我们完成了栈的类模板定义。

接下来,我们来测试一下,这个类模板,看看它能不能达到我们想要的结果。

让我们一起来认识一下C++的模板_实例化_22

让我们一起来认识一下C++的模板_函数模板_23

从结果看,程序能够正常运行,并得出正确的结果。类模板也能根据不同的类型实例化出不同的栈。这里需要注意的是,类模板实例化与函数模板实例化不同,类模板实例化需要在类模板名字后跟<>,然后将实例化的类型放在<> 中即可,类模板名字不是真正的类,而实例化的结果才是真正的类。

好了,到这里,我们本次的分享就到此结束了,不知道我有没有说明白,给予你一点点收获。关于更多和模板相关的知识,会在后面的文章更新。如果你有所收获,别忘了给我点个赞,这是对我最好的回馈,当然你也可以在评论发表一下你的收获和心得,亦或者指出我的不足之处。如果喜欢我的分享,别忘了给我点关注噢。