#include<iostream>usingnamespace std;#include<string>/*
函数模板
C++另一种编程思想称为:泛型编程,主要利用的技术就是模板
C++提供两种模板机制:
1.函数模板
2.类模板
函数模板语法:
template<typename T>
函数声明或定义
template --- 声明创建模板
typename --- 表明其后面的符号是一种数据类型,typename可以用class代替
T --- 通用的数据类型,名称可以替换,通常为大写字母
函数模板作用:
建立一个通用函数,其函数返回值类型和形参类型可以不具体制定,用一个虚拟的类型来代表
提高代码的复用性
*///1.实现两个整形交换的函数(使用引用的方式传递,实现实参交换)voidswapInt(int&a,int&b){int temp;
temp = a;
a = b;
b = temp;}//2.实现两个浮点型交换的函数voidswapFloat(float&a,float&b){float temp;
temp = a;
a = b;
b = temp;}//3.创建函数模板template<typename T>//声明一个模板,让后面的T不报错,T为通用数据类型//函数的声明和定义voidmySwap(T &a, T &b){
T temp;
temp = a;
a = b;
b = temp;}voidTest01(){//不用模板,需要定义两个交换函数int a =10;int b =20;//swapInt(a, b);float c =10;float d =20;//swapFloat(c, d);//下面使用函数交换的模板//使用模板有两种方式:1.自动类型推倒,2.显示指定类型//1.自动类型推倒mySwap(a, b);
cout <<"a="<< a <<" b="<< b << endl;//2.显示指定类型
mySwap<float>(c, d);
cout <<"c="<< c <<" d="<< d << endl;}intmain(){Test01();system("pause");return0;}
03C++提高编程—模板(函数模板的注意事项)
#include<iostream>usingnamespace std;#include<string>/*
函数模板注意事项
注意事项:
1.自动类型推导,必须推导出一致的数据类型T,才可以使用
2.模板必须要确定出T的数据类型,才可以使用
*///1.自动类型推导,必须推导出一致的数据类型T,才可以使用template<classT>voidmySwap(T &a,T &b){
T temp;
temp = a;
a = b;
b = temp;}//2.模板必须要确定出T的数据类型,才可以使用template<classT>voidfunc(){
cout <<"func被调用"<< endl;}voidTest01(){int a =10;int b =20;char c ='c';mySwap(a, b);
cout <<"a="<< a <<" b="<< b << endl;//mySwap(a, c);//自动类型推导,数据类型不一致,报错}voidTest02(){//模板必须要确定出T的数据类型,这里没有也必须指定(随便指定一个),否则报错//func();
func<int>();}intmain(){Test01();Test02();system("pause");return0;}
04C++提高编程—模板(小案例)
#include<iostream>usingnamespace std;#include<string>/*
案例描述:
1.利用函数模板封装一个排序的函数,可以对不同数据类型数组进行排序
2.排序规则从大到小,排序算法为选择排序
3.分别利用char数组和int数组进行测试
选择排序:
第一次从待排序的数据元素中选出最小(或最大)的一个元素,
存放在序列的起始位置,然后再从剩余的未排序元素中寻找到最小(大)元素,
然后放到已排序的序列的末尾。以此类推,直到全部待排序的数据元素的个数为零
*///1.定义一个两个元素交换的函数模板template<classT>voidmySwap(T &a,T &b){
T temp;
temp = a;
a = b;
b = temp;}//2.定义一个数组排序的函数模板(sort:排序)template<classT>voidmySort(T arr[],int len){for(int i =0; i < len; i++){
T max = i;//定义最大值得下标for(int j = i +1; j < len; j++)//让第一个数和后面的比较{if(arr[max]< arr[j]){
max = j;//交换下标,是max拿到最大值得下标}}if(max != i)//把最大值放在第一位{mySwap(arr[i], arr[max]);}}}//打印数组模板函数template<classT>voidmyPrint(T arr[],int len){for(int i =0; i < len; i++){
cout <<"arr["<< i <<"]="<< arr[i]<< endl;}}voidtest01(){//测试char数组char charArr[]="bdcfeagh";int num =sizeof(charArr)/sizeof(char);
mySort<char>(charArr, num);//显示myPrint(charArr, num);//自动类型推导}voidtest02(){//测试int数组int intArr[]={1,7,9,8,5,3,6,7,9,5};int num =sizeof(intArr)/sizeof(int);mySort(intArr, num);myPrint(intArr, num);}intmain(){test01();
cout <<"-------------"<< endl;test02();system("pause");return0;}
05C++提高编程—模板(普通函数和模板的区别)
#include<iostream>usingnamespace std;#include<string>/*
普通函数与函数模板区别:
1.普通函数调用时可以发生自动类型转换(隐式类型转换)
2.函数模板调用时,如果利用自动类型推导,不会发生隐式类型转换
3.如果利用显示指定类型的方式,可以发生隐式类型转换
建议使用显示指定类型的方式,调用函数模板,因为可以自己确定通用类型T
*///普通函数intmyAdd01(int a,int b){return a + b;}//函数模板template<classT>
T myAdd02(T a, T b){return a + b;}//使用函数模板时,如果用自动类型推导,不会发生自动类型转换,即隐式类型转换voidtest01(){int a =10;int b =20;char c ='c';
cout <<myAdd01(a, c)<< endl;//正确,将char类型的'c'隐式转换为int类型 'c' 对应 ASCII码 99//myAdd02(a, c); // 报错,使用自动类型推导时,不会发生隐式类型转换
myAdd02<int>(a, c);//正确,如果用显示指定类型,可以发生隐式类型转换
cout << myAdd02<int>(a, c)<< endl;}intmain(){test01();system("pause");return0;}
06C++提高编程—模板(普通函数和模板调用规则)
#include<iostream>usingnamespace std;#include<string>/*
调用规则如下:
1. 如果函数模板和普通函数都可以实现,优先调用普通函数
2. 可以通过空模板参数列表来强制调用函数模板
3. 函数模板也可以发生重载
4. 如果函数模板可以产生更好的匹配,优先调用函数模板
总结:既然提供了函数模板,最好就不要提供普通函数,否则容易出现二义性
*/voidmyPrint(int a,int b){
cout <<"调用的普通函数"<< endl;}template<typename T>voidmyPrint(T a, T b){
cout <<"调用的模板"<< endl;}template<typename T>voidmyPrint(T a, T b, T c){
cout <<"调用重载的模板"<< endl;}voidtest01(){//1、如果函数模板和普通函数都可以实现,优先调用普通函数// 注意 如果告诉编译器,普通函数是有的,但只是声明没有实现,或者不在当前文件内实现,就会报错找不到int a =10;int b =20;myPrint(a, b);//调用普通函数//2、可以通过空模板参数列表来强制调用函数模板
myPrint<>(a, b);//调用函数模板//3、函数模板也可以发生重载int c =30;myPrint(a, b, c);//调用重载的函数模板//4、如果函数模板可以产生更好的匹配,优先调用函数模板//因为普通函数提供的是整型的数据,函数模板可以自动匹配出字符型数据char c1 ='a';char c2 ='b';myPrint(c1, c2);//调用函数模板}intmain(){test01();system("pause");return0;}
07C++提高编程—模板(模板的局限性)
#include<iostream>usingnamespace std;#include<string>/*
模板的局限性:模板的通用性并不是万能的
例如:
template<class T>
void f(T a, T b)
{
a = b;
}
上述代码中提供的赋值操作,如果传入的a和b是一个数组,就无法实现了
例如:
template<class T>
void f(T a, T b)
{
if(a > b) { ... }
}
在上述代码中,如果T的数据类型传入的是像Person这样的自定义数据类型,也无法正常运行
因此C++为了解决这种问题,提供模板的重载,可以为这些,特定的类型,提供具体化的模板
总结:
利用具体化的模板,可以解决自定义类型的通用化
学习模板并不是为了写模板,而是在STL能够运用系统提供的模板
*///定义一个类classPerson{public://定义有参构造Person(string name,int age){//那个对象调用有参构造,this指针就代表那个对象实体,然后把传过来的参数赋值给对象的属性this->m_Name = name;this->m_Age = age;}
string m_Name;int m_Age;};//普通模板template<classT>boolmyCompare(T &a,T &b){if(a == b){returntrue;}else{returnfalse;}}//具体化,显示具体化的原型和定意思以template<>开头,并通过名称来指出类型//具体化优先于常规模板template<>boolmyCompare(Person &p1, Person &p2){if(p1.m_Age == p2.m_Age&&p1.m_Name == p2.m_Name){returntrue;}else{returnfalse;}}voidtest01(){int a =10;int b =20;//内置数据类型可以直接使用通用的函数模板bool ret =myCompare(a, b);if(ret){
cout <<"a == b "<< endl;}else{
cout <<"a != b "<< endl;}}voidtest02(){
Person p1("Tom",10);
Person p2("Tom",10);//自定义数据类型,不会调用普通的函数模板//可以创建具体化的Person数据类型的模板,用于特殊处理这个类型bool ret =myCompare(p1, p2);if(ret){
cout <<"p1 == p2 "<< endl;}else{
cout <<"p1 != p2 "<< endl;}}intmain(){test01();test02();system("pause");return0;}