第20课 - 函数模板
1. C++中如何交换两个变量的值?
void Swap(int& a, int& b)
{
int t = a;
a = b;
b = t;
}
void Swap(float& a, float& b)
{
float t = a;
a = b;
b = t;
}
void Swap(char *& a, char *& b)
{
char *& t = a;
a = b;
b = t;
}
除了类型不同,函数体代码完全相同。C++强调代码复用,那如何来解决代码冗余的问题呢?
2. 泛型编程
不考虑具体类型的编程模式。我们先考虑算法,再考虑类型。
对于Swap函数可以考虑下面的泛型写法
void Swap(T& a, T& b)
{
T t = a;
a = b;
b = t;
}
Swap泛型写法中的T不是一个具体的数据类型,而是泛指任意的数据类型。
(1)函数模板
提供了一种特殊的函数可用不同类型进行调用,看起来和普通的函数很相似,区别是类型可被参数化。
template <typename T>
void Swap(T& a, T& b)
{
T t = a;
a = b;
b = t;}
(2)函数模板的语法规则
template关键字用于声明开始进行泛型编程。
typename关键字用于声明泛指类型。
(3)函数模板的应用
自动类型推导调用
具体类型显示调用
#include <cstdlib>
#include <iostream>
using namespace std;
template<typename T>
void Swap(T& a, T& b)
{
T t = a;
a = b;
b = t;
}
int main(int argc, char *argv[])
{
int a = 1;
int b = 2;
Swap(a, b); //自动类型推导:a和b均是int,因此类型参数T为int
cout<<"a = "<<a<<endl;
cout<<"b = "<<b<<endl;
float fa = 3;
float fb = 4;
Swap<float>(fa, fb); //显示类型调用:用loat替换参数类型T
cout<<"fa = "<<fa<<endl;
cout<<"fb = "<<fb<<endl;
char ca = 'a';
char cb = 'b';
Swap(ca, cb);
cout<<"ca = "<<ca<<endl;
cout<<"cb = "<<cb<<endl;
cout << "Press the enter key to continue ...";
cin.get();
return EXIT_SUCCESS;
}
运行结果:
a = 2
b = 1
fa = 4
fb = 3
ca = b
cb = a
3. 泛型编程初体验(排序算法)
#include <cstdlib>
#include <iostream>
using namespace std;
template<typename T>
void Swap(T& a, T& b)
{
T t = a;
a = b;
b = t;
}
template<typename T>
void SelectSort(T array[], int length)//选择排序
{
for(int i = 0; i<length; i++) //全循环
{
T min = array[i];
int index = i;
for(int j = i+1; j < length; j++)
{
if( array[j] < min) //找最小
{
min = array[j];
index = j;
}
}
Swap(array[i],array[index]);
}
}
int main(int argc, char *argv[])
{
int array[] = {3, 2, 5, 3 , 4};
SelectSort<int>(array, 5);
for(int i=0; i<5; i++)
{
cout<<array[i]<<endl;
}
char ca[] = {'b', 'c', 'a', 'e', 'd', 'f'};
SelectSort(ca, 6);
for(int i=0; i<6; i++)
{
cout<<ca[i]<<endl;
}
cout << "Press the enter key to continue ...";
cin.get();
return EXIT_SUCCESS;
}
运行结果:
2
3
3
4
5
a
b
c
d
e
f
l 函数模板的深入理解:
编译器并不是把函数模板处理成为能够处理任意类型的函数。
编译器从函数模板通过具体类型产生不同的函数。(用几个类型产生几个函数)
编译器会对函数模板进行两次编译:在声明的地方对模板代码本身进行编译,在调用的地方对参数替换后的代码进行编译。
问:
当函数模板遇到函数重载会发生什么?
4. 函数模板与函数重载
函数模板可以像普通函数一样被重载。C++编译器优先考虑普通函数,如果函数模板可以产生一个更好的匹配,那么选择模板。可以通过空模板实参列表的语法限定编译器只通过模板匹配。
#include <cstdlib>
#include <iostream>
using namespace std;
int Max(int a, int b) //与函数模板重名,这两者重载。 这两个函数不会发生冲突。
{
cout<<"int Max(int a, int b)"<<endl;
return a > b ? a : b;
}
template<typename T> //优先考虑普通函数。
T Max(T a, T b)
{
cout<<"T Max(T a, T b)"<<endl;
return a > b ? a : b;
}
template<typename T>
T Max(T a, T b, T c)
{
cout<<"T Max(T a, T b, T c)"<<endl;
return Max(Max(a, b), c);
}
int main(int argc, char *argv[])
{
int a = 1;
int b = 2;
cout<<Max(a, b)<<endl;
cout<<Max<>(a, b)<<endl;
cout<<Max(3.0, 4.0)<<endl;
cout<<Max(5.0, 6.0, 7.0)<<endl;
cout<<Max('a', 100)<<endl; //不允许自动类型转换,只能调用普通函数。
cout << "Press the enter key to continue ...";
cin.get();
return EXIT_SUCCESS;
}
运行结果:
int Max(int a, int b)
2
T Max(int a, int b)
2
T Max(int a, int b)
4
T Max(T a, T b, T c)
T Max(int a, int b)
T Max(int a, int b)
7
int Max(int a, int b)
100
注意事项:
函数模板不允许自动类型转化,普通函数能够进行自动类型转换。
cout<<Max(‘a’, 100)<<endl; 必然只会调用普通函数。
5. 多参数函数模板
函数模板可以定义任意多个不同的类型参数
template<typename T1, typename T2, typename RT>
RT Add(T1 a, T2 b)
{
return static_cast<RT>(a + b)
}
cout<<Add<char, float, double >(‘a’, 100)<<endl;
问题:
多类型参数的模板可以进行自动类型推导吗?
当生命的类型参数为返回值类型时,无法进行自动类型推导。
#include <cstdlib>
#include <iostream>
using namespace std;
template<typename RT, typename T1, typename T2>
RT Add(T1 a, T2 b)
{
return static_cast<RT>(a + b);
}
int main(int argc, char *argv[])
{
cout<<Add<double, char, float>('a', 100.0f)<<endl;
cout<<Add<double>('a', 100.0f)<<endl; //只定义返回值类型
//cout<<Add('a', 100.0f)<<endl; 这样写会报错
cout << "Press the enter key to continue ...";
cin.get();
return EXIT_SUCCESS;
}运行结果:
197
197
l 不完美的解决方案
将返回值类型参数声明到第一个参数的位置,调用时只需显示声明反水类型参数即可。
小结:
函数模板起始是一个具有相同行为的函数家族。
函数模板可以根据类型实参对函数进行推导调用。
函数模板可以显示指定的类型参数。
函数模板可以被重载。