C++学习(15)—— 函数模板
- C++另一种编程思想称为泛型编程,主要利用的技术就是模板
- C++提供两种模板机制:函数模板和类模板
1.函数模板语法
函数模板作用:
建立一个通用函数,其函数返回值类型和形参类型可以不具体定制,用一个虚拟的类型来代表
语法:
template<typename T>
函数定义或声明
解释:
template —— 声明创建模板
typename —— 表明其后面的符号是一种数据类型,可以用class代替
T —— 通用的数据类型,名称可以替换,通常为大写字母
#include<iostream>
using namespace std;
//函数模板
//两个整型交换的函数
void swapInt(int &a,int &b){
int temp = a;
a = b;
b = temp;
}
//两个浮点型交换的函数
void swapDouble(double &a,double &b){
double temp = a;
a = b;
b = temp;
}
//函数模板
template<typename T> //声明一个模板,告诉编译器后面代码中紧跟着的T不要报错,T是一个通用数据类型
void mySwap(T &a,T &b){
T temp = a;
a = b;
b = temp;
}
void test01(){
int a = 10;
int b = 20;
swapInt(a,b);
//两种方式使用函数模板
//1.自动类型推导
mySwap(a,b);
//2.显示指定类型
mySwap<int>(a,b);
cout << "a = " << a <<endl;
cout << "b = " << b <<endl;
double c = 1.1;
double d = 2.2;
swapDouble(c,d);
cout << "c = " << c <<endl;
cout << "d = " << d <<endl;
}
int main(){
test01();
return 0;
}
总结:
- 函数模板利用关键字template
- 使用函数模板有两种方式:自动类型推导、显示指定类型
- 模板的目的是为了提高复用性,将类型参数化
2.函数模板注意事项
注意事项:
- 自动类型推导,必须推导出一致的数据类型T才可以使用
- 模板必须要确定出T的数据类型,才可以使用
//错误,推导不出一致的T类型
int a = 10;
char b = 'c';
mySwap(a,b);
template<class T>
void func(){
cout << "func调用" << endl;
}
void test02(){
func(); //错误,编译器无法确定模板的T类型
func<int>();
}
3.函数模板案例
案例描述:
- 利用函数模板封装一个排序的函数,可以对不同数据类型数组进行排序
- 排序规则从大到小,排序算法为选择排序
- 分别利用char数组和int数组进行测试
#include<iostream>
using namespace std;
//实现通用 对数组进行排序的函数
//规则 从大到小
//算法 选择
//测试 char数组、 int数组
//交换函数模板
template<class T>
void mySwap(T &a, T &b){
T temp = a;
a = b;
b = temp;
}
//排序算法
template<class T>
void mySort(T arr[], int len){
for(int i=0;i<len;i++){
int max = i;//认定最大值的下标
for(int j=i+1;j<len;j++){
if(arr[max]<arr[j]){
max = j;
}
}
if(max!=i){
//交换
mySwap(arr[max],arr[i]);
}
}
}
//打印函数模板
template<class T>
void myPrint(T arr[], int len){
for(int i=0;i<len;i++){
cout<<arr[i]<<" ";
}
cout<<endl;
}
//测试char数组
void test01(){
char charArr[]="badcfe";
int len = sizeof(charArr) / sizeof(charArr[0]);
mySort(charArr,len);
myPrint(charArr,len);
}
//测试int数组
void test02(){
int intArr[]={7,5,1,3,9,2,4};
int len = sizeof(intArr) / sizeof(intArr[0]);
mySort(intArr,len);
myPrint(intArr,len);
}
int main(){
test02();
return 0;
}
4.普通函数与函数模板的区别
普通函数与函数模板区别:
- 普通函数调用时可以发生自动类型转换(隐式类型转换)
- 函数模板调用,如果利用自动类型推导,不会发生隐式类型转换
- 如果利用显示指定类型的方式,可以发生隐式类型转换
#include<iostream>
using namespace std;
//普通函数与函数模板区别
//普通函数
int myAdd01(int a, int b){
return a+b;
}
//函数模板
template<class T>
T myAdd02(T a, T b){
return a+b;
}
void test01(){
int a = 10;
int b = 20;
char c = 'c';
cout << myAdd01(a,c) << endl;
cout << myAdd02<int>(a,c) << endl;
}
int main(){
test01();
return 0;
}
总结:建议使用显示指定类型的方式,调用函数模板,因为可以自己确定通用类型T
5.普通函数与函数模板的调用规则
调用规则如下:
- 如果函数模板和普通函数都可以实现,优先调用普通函数
- 可以通过空模板参数列表来强制调用函数模板
- 函数模板也可以发生重载
- 如果函数模板可以产生更好的匹配,优先调用函数模板
#include<iostream>
using namespace std;
//普通函数与函数模板调用规则
//普通函数
void myPrint(int a, int b){
cout << "1" << endl;
}
//函数模板
template<class T>
void myPrint(T a, T b){
cout << "2" << endl;
}
template<class T>
void myPrint(T a, T b, T c){
cout << "3" << endl;
}
void test01(){
int a = 10;
int b = 20;
char c = 'c';
char d = 'd';
myPrint(a,b);
myPrint<>(a,b);
myPrint(a,c);
myPrint(c,d);
myPrint(a,b,100);
}
int main(){
test01();
return 0;
}
总结:既然提供了函数模板,最好就不要提供普通函数,否则容易出现二义性
6.模板的局限性
局限性:
- 模板的通用性并不是万能的
例如:
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++为了解决这种问题,提供模板的重载,可以为这些特定的类型提供具体化的模板
#include<iostream>
#include<string>
using namespace std;
//模板的局限性
class Person{
public:
Person(string name,int age){
this->m_name = name;
this->m_age = age;
}
string m_name;
int m_age;
};
//对比两个数据是否相等的函数
template<class T>
bool myCompare(T &a, T &b){
if(a == b){
return true;
}
return false;
}
//利用具体化Person的版本实现代码,具体化优先调用
template<> bool myCompare(Person &p1, Person &p2){
if(p1.m_name == p2.m_name &&p1.m_age == p2.m_age){
return true;
}
return false;
}
void test01(){
int a = 10;
int b = 20;
if(myCompare(a,b)){
cout<<"a==b"<<endl;
}else{
cout<<"a!=b"<<endl;
}
}
void test02(){
Person p1("Tom", 10);
Person p2("Tom", 11);
if(myCompare(p1,p2)){
cout<<"p1==p2"<<endl;
}else{
cout<<"p1!=p2"<<endl;
}
}
int main(){
test02();
return 0;
}
总结:
- 利用具体化的模板,可以解决自定义类型的通用化
- 学习模板并不是为了写模板,而是在STL能够运用系统提供的模板
Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it.