模板初阶:
我们今天来学习一下模板:
我们的模板分为函数模板和类模板;
我们首先来学习一下函数模板;
泛型编程:
我们先看一下下面的代码:
我们看,当我们使用我们的交换Swap函数的时候,我们会构成很多的函数重载,当我们进行数据的交换的时候,我们使用swap函数来进行,但是我们有时候交换的数据是int类型的,有时候是double类型的,这时候我们想要交换他们的数据,这时候我们就要创建很多的swap函数来构成函数重载;
那我们有没有什么办法可以解决我们的这个问题,因为我们的这些个函数都是一样的,只是他的参数有差异,有办法,我们今天就来学习一下模板;
1. 函数模板:
函数模板的格式:
我们的这个函数的模式就是我们的交换函数的模板。
函数模板的原理:
我们的函数模板不是一个函数,他只是一个蓝图,我们的编译器推演实例化出来其他的函数,我们的模板是写给我们的编译器的。
函数模板的实例化:
函数模板的实例化,我们首先来看隐式实例化:
我们看我们的代码,我们传参数,然后让我们的编译器来判断:我们的第一个函数我们传的参数为int类型的参数,这时候我们的编译器就会根据我们的这个参数类型推演我们的模板参数的类型为int,然后我们的下面的函数我们的参数又是double类型的,这时候我们的编译器就会根据这个参数来推断我们的模板的参数为double类型的。
但是有可能我们在传参的时候会出现下面的问题:
我们看我们上面的图片:当我们的函数的参数一个是int类型的,一个是double类型的时候,这时候我们的编译器就无法判断我们的函数模板的参数到底是什么类型的,因为我们的函数模板的参数类型都是T,这时候我们就不能判断我们的T到底是int还是double,函数模板的参数都是T,他们必须要保持一致。
所以这时候编译器就会报错;
所以这时候有两种方式来进行处理,
1. 我们对其中一个参数进行强制类型转换,让他们两个参数类型一样;
2. 使用显示实例化;
我们看我们的显式实例化:我们看我们上面的代码:
我们的参数一个是int类型的,一个是double类型的,这时候我们想要编译通过不报错,我们的另外的选择是在函数名的后面加上一个尖括号,然后里面写上类型,这时候这两个参数的类型都是int类型,这时候编译就没问题了;
模板参数的匹配原则:
我们看上面,我们定义的加法函数和我们的模板函数是可以同时存在的。
我们的函数调用的选择是,有成品的我们就吃成品的,我们看上面的代码,我们有现成的加法函数,我们直接调用就可以,就不需要我们的编译器再推演生成一个加法函数了。
你看我们的下面的Add函数,我们其实两个函数都是可以调用的,对于现成的加法函数,我们的浮点数类型会被转化成int类型的来进行处理。
但是当他们两个函数都可以调用的时候,我们会选择更匹配他们的来进行选择。
2. 类模板:
我们来看我们的类模板:
我们来看我们的代码:我们的类模板:我们把里面的指针类型设置为T,就是我们的栈里面的指向动态申请内存的空间为T类型的,我们的T可以替换为int,double等类型。
类模板的实例化:
我们看了上面的类模板,这时候就会有一个问题,那就是我们之前的函数模板,我们使用的时候我们的编译器会根据我们的实参来实例化推演出我们的函数的参数,但是对于我们的类模板,我们怎么知道我们类里面(就比如我们上面设置的栈,指向的内存空间是什么类型的呢?)
类模板的实例化这时候就用到了:
因为我们的类模板是推不出来我们的T到底是什么类型的,所以类模板都是显示实例化。
我们使用类的实例化,我们在我们的后面加上尖括号,然后尖括号里面加上类型,这时候我们的类里面的T就是我们的尖括号里面的类型。