第一部分 --- 模板的概念
模板就是建立一个通用的模具,大大提高复用性
模板的特点:
1.模板的通用性很强
2.模板不能够直接使用,我们必须得给模板添加一些东西后才能够使用这个模板
3.模板不是万能的,它有自己的适用范围
比如说上面的一寸照片:
1.每一个需要一寸照片的的人都能够使用这个一寸照片模板 --- 通用性强
2.拿到一寸照片模板后我们必须把自己的头像P进去,而不是直接使用模板 --- 要填内容
3.给狗准备一寸照片的时候不能够使用上面这个照片模板 --- 模板具有适用范围
第二部分 --- 函数模板基本用法
c++中有两种编程思想:1.面向对象编程;2.泛型编程
其中泛型编程主要利用的技术是模板
这个虚拟的类型就是泛型
将函数的返回值类型和形参列表的参数的类型抽象为泛型,所谓的泛型是一个不确定的类型,他就像一个类型变量一样,我们在调用函数的时候赋予泛型什么类型,他就表现出这个类型的性质。
创建函数模板的语法:
template:模板关键字,声明创建模板
声明好模板和泛型变量后,在其下一行写的函数声明/函数定义对应的函数就是函数模板
函数模板的应用:最典型就是实现两个数交换的函数模板。如果用正常的函数来实现两数交换的话,由于两个数的类型有很多种,所以我们也要重复的写很多只有类型不一样,但实现逻辑和代码一模一样的交换函数。
但是使用了函数模板之后,我们可以用泛型来代替具体的类型,然后用泛型来写交换函数的实现代码,等具体调用函数的时候,只需要将具体的类型传给泛型后就能使用这个函数模板实现两数交换,这和用普通的函数实现相比就减少了很多的冗余代码,增加了代码的复用性。
上面这就是一个函数模板的实现
关于函数模板的使用:我们有两种方式来使用函数模板
1.自动类型推导
调用函数模板的时候直接传参数,编译器会根据参数自动推导出其类型,并将其赋值给泛型变量T
2.显示指定类型
直接指定一个类型给泛型变量T赋值,不需要编译器自己去推导
在函数名后面加 <指定给泛型变量T的类型>(参数列表)
所谓的泛型其实就是上面讲的:类型参数化
第三部分 --- 函数模板注意事项
第一点:什么叫做必须推导出一致的数据类型T呢? --- 就是必须得保证我们在用自动类型推导调用函数模板的时候,传的参数的类型必须一样,这样字编译器才能够推导出一样的数据类型
这样要求的原因是我们的泛型变量T只能承接一个类型,也只能够表现出一个类型的性质,如果同时传多个类型泛型变量T的话就会导致程序错误。
第二点:如果要调用函数模板的话,不管有没有用到泛型变量T,我们都必须给泛型变量T赋一个类型(通过直接指定赋值<类型>),不然的话会调用函数模板失败,程序报错。
第四部分 --- 函数模板案例 --- 数组排序
在函数模板中,不仅可以用泛型变量T作为参数,也可以用其它类型的变量作为参数,如上图
(补充知识:数组指针的创建:)
p就是一个数组指针,它指向的是数组,它解引用后得到的是数组首元素的地址,数组的类型是
数组元素的类型 [ 数组的元素个数 ] ----- 创建数组指针的方式是 元素类型(*p)[元素个数]
一般我们指向一个数组很少采用上面这种方式,主要使用的方式是传数组首元素的地址(数组名)以及数组的元素个数,通过这种方式在函数中访问函数外创建的数组)
第五部分 --- 普通函数与函数模板的区别
第六部分 --- 普通函数与函数模板的调用规则
普通函数和函数模板之间能够发生函数重载
如果函数模板和普通函数都能够实现的话,优先选择普通函数,如果想强制让函数模板来实现的话,可以通过空模板参数列表来实现:
写了上面这个之后会优先调用函数模板
上面这个就反应了第三点,函数模板之间能够发生函数模板重载(函数模板与普通函数之间也能够发生函数重载)
什么叫做更好的匹配呢? --- 就是当我们向函数传参的时候,如果参数能够不进行类型转换的话这个函数的匹配就更好
比如上面这个,如果普通函数只能够接受整型参数的话,我们传字符类型的话就会发生隐式类型转换,但是当我们用函数模板的时候,就是直接用字符类型来接受,两相比较肯定是函数模板的匹配更好,所以选择调用函数模板。
总结一句话:如果用了函数模板就不要用普通函数了,避免二义性出现
第七部分 --- 模板的局限性
那么我们如何处理这种局限性呢? --- 泛型编程为我们提供了一项技术来处理这个问题:
使用函数模板重载,直接为特定的类型特例化一个模板出来,当我们向泛型变量T传特定的类型的时候,编译器就会自动去调用我们为这个特定的类型专门准备的重载模板
比如下面这个例子
上面这个模板是普通的模板,它能够处理C++中内置的数据类型,但是它处理不了我们自定义的类型 ---- 此时我们自定义一个Person类型,示例化两个对象调用模板进行比较的话编译器会报错,因为函数模板内的运算符无法对这两个对象进行比较(除非我们用运算符重载)
为了解决这个问题,我们使用函数模板重载,专门为Person类型准备一个模板调用:
重载的关键是我们要用 template<> 修饰被重载的函数模板 ,然后函数模板的参数要直接使用特定的类型创建,然后这个函数模板中的实现是专门特定类型的对象准备的
当我们给函数模板传特定的对象的时候,编译器就会自动调用专门为这个特定类型准备的函数模板