今天,和大家分享与模板相关的知识,本次内容主要为函数模板和类模板。
泛型编程
大家还记得怎么去实现交换函数吗?
我们以int类型作为例子,实现出一个交换函数。
我们来测试一下:
从运行结果看,交换函数的功能并没有什么问题。
好了,我们有了int类型的交换函数了。可是,现在我不想交换int类型的数据了,我想交换double类型的数据了,这个函数能实现吗?我们把变量a和变量b的类型换成double看看。
很明显,编译器报错了。这时候,就有同学说啦。这int类型的交换函数怎么能交换double类型呢?我们需要实现一个double类型的交换函数才行。
确实,当我们重载double类型的交换函数后,我们就能实现double类型的数据交换了。可如果现在我不想交换int类型,也不想交换double类型,就想交换float类型的数据。那程序是不是又不行了。
这时,又有同学说了,我们可以再加一个float类型的交换函数呀!这样的方法可行吗?当然可行,但是每当我们进行原来没有对应交换函数的数据类型交换时,就需要新增一个交换函数重载。
不知道大家,有没有见过金属器件制造的过程。
在金属工艺中,大多数金属制品都会有一个膜具,不管你想要什么材料的都行,只需要把材料加进膜具里就可以得到不同材质的铸件。
在C++里也有这样类似的膜具,把不同的材料(类型)加进去,生成不同的铸件(即具体的代码)。那什么是泛型编程呢?泛型编程就是编写与类型无关的通用代码,它是一种代码复用的手段,以模板作为基础。
函数模板
模板包含有函数模板和类模板两个。
我们先来了解函数模板。
函数模板的定义
在定义函数模板前,我们需要定义模板参数。
模板参数的定义的定义方法是template关键字加尖括号,在尖括号中,用class或typename(这两个都可以用)加上模板参数的名字即可。如果你有多个模板参数的话,以逗号作为分割符。
下面,我们来演示一下:
以上就是参数列表的定义方法。它是函数模板的基础,在此基础上加上函数的实现,一个函数模板就完成了。
编译器并不允许定义这么多的模板参数列表,我们把它先注释掉,然后,根据函数模板的格式,完成一个交换函数的定义
我们把之前实现的交换函数注释掉后,来测试一下:
从结果看,没什么问题,float类型的数据完成了交换,我们把float类型换成double类型再进行测试:
从结果看,没什么问题。
像上面Swap这样的函数,我们称之为模板函数。
模板函数的实现的原理,是让编译器通过传过来的参数类型来自动推导实例出相应的函数。
然后,完成相应的交换功能。
我们将函数模板推导成对应类型的函数称之为函数模板实例化。
函数模板的实例化
函数模板的实例化又分为隐式实例化和显示实例化。由于交换函数比较特殊,我们以两个数相加的函数为例演示:
我们先来看看两个整型的相加:
从程序结果来看,程序是能够正常运行并得出正确结果的。
接下来,我们把相加的数换成doule类型和int类型的,测试一下:
很显然,编译器报错了。这是为什么呢?这是因为函数模板无法自动的进行类型的转换,也就是它不知道模板参数该实例化成double类型还是int类型的函数。
那我们该怎么去解决这个问题呢?既然编译器不知道该实例化成什么类型,那我们可以告诉它不就好了吗?对于这一块的问题,我们可以用一下两种方式告诉编译器该实例化成什么类型。
第一种:对变量b进行强制类型转换。
第二种:显示实例化,方式是在函数后面增加一个尖括号,尖括号里面填要实例化的类型。
前面我们学过函数是可以重载的,如果模板函数遇到了同名的函数,会怎么处理呢?
为了便于我们确认编译器到底使用那一个函数,我们做一些特殊的处理。
做完特殊处理后,我们来测试一下:
很明显,编译器并没有使用函数模板,而是使用了与函数模板同名的Add函数。这是为什么?对于与函数模板同名的函数,相当于函数模板的特化。图中的特化是对象是int整形,对于int类型这种情况,我们特殊对待走我们实现的函数,而不会走函数模板。
对于函数模板特化的作用,我们在后面的章节再细细讲述。
在学习数据结构的时候,相信大家都模拟过栈。
当我们模拟一个int类型的栈,是不是类似下面这样。
我们这样实现的栈,可以对int类型进行操作,可当我们把数据的类型换成了double类型,我们的栈是不是就不能用了?这时候,有些同学就会说啦,我们可以再实现一个double类型的栈呀。
对,我们再实现一个double类型栈,问题就解决。可是类型不仅有自定义类型还有自定义类型,难道我们都要一一去实现它吗?
这显然是很麻烦的,那我们有没有什么方法去写一个通用的类呢?
当然有,我们把这样通用的类称之为类模板。
类模板
类模板的定义方式与函数模板的定义方式有一些类似,需要先定义出参数列表,然后,再完成相应的函数和成员。
通过类模板的定义方式,我们完成了栈的类模板定义。
接下来,我们来测试一下,这个类模板,看看它能不能达到我们想要的结果。
从结果看,程序能够正常运行,并得出正确的结果。类模板也能根据不同的类型实例化出不同的栈。这里需要注意的是,类模板实例化与函数模板实例化不同,类模板实例化需要在类模板名字后跟<>,然后将实例化的类型放在<> 中即可,类模板名字不是真正的类,而实例化的结果才是真正的类。
好了,到这里,我们本次的分享就到此结束了,不知道我有没有说明白,给予你一点点收获。关于更多和模板相关的知识,会在后面的文章更新。如果你有所收获,别忘了给我点个赞,这是对我最好的回馈,当然你也可以在评论发表一下你的收获和心得,亦或者指出我的不足之处。如果喜欢我的分享,别忘了给我点关注噢。