1.引入模版
如何写出一个通用的交换函数?
void Swap(int& left, int& right)
{
int temp = left;
left = right;
right = temp;
}
void Swap(double& left, double& right)
{
double temp = left;
left = right;
right = temp;
}
void Swap(char& left, char& right)
{
char temp = left;
left = right;
right = temp;
}
上述代码使用函数重载,基本达到要求。
但是使用函数重载虽然可以实现,但是有一下几个不好的地方:
1. 重载的函数仅仅是类型不同,代码复用率比较低,只要有新类型出现时,就需要用户自己增加对应的函数;
2. 代码的可维护性比较低,一个出错可能所有的重载均出错;对于下列浇筑,需制作不同的颜色的铸件,我们仅需给一个模具中填充不同的材料(类型)即可
而在C++中也存在着这样的一个模具。因此我们引入的模版这个概念,使用模板就可以减小很多工作量,而模板又分为函数模板和类模板
泛型编程:
编写与类型无关的通用代码,是代码复用的一种手段。
模板是泛型编程的基础
2.函数模版
template <class 形参名,class 形参名,......> 返回类型 函数名(参数列表)
{
函数体
}
其中template和class是关键字,class可以用template关键字代替,在这里template和class没区别,<>括号中的参数叫模板形参,模板形参和函数形参很相像,模板形参不能为空。一但声明了模板函数就可以用模板函数的形参名声明类中的成员变量和成员函数,即可以在该函数中使用内置类型的地方都可以使用模板形参名。
但不可以使用struct来代替class
对于上述代码,我们可以利用函数模版来减轻我们的工作量,代码如下,
//template<typename T> template<class T> void Swap( T& left, T& right) { T temp = left; left = right; right = temp; }
其中T是参数类型,可以变换。
在编译阶段,对于模板函数的使用,编译器需要根据传入的实参类型来推演生成对应类型的函数以供调用。比如:当用double类型使用函数模板时,编译器通过对实参类型的推演,将T确定为double类型,然后产生一份专门处理int类型的代码,对于char类型也是如此。
模版将我们应该做的事情交给了编译器去做!
编译器:谢谢你嘞!
总而言之,函数模版,,,模版,模版就是一个模型。
大家在生活中,有没有做过雪糕,月饼呢?在制作的时候都会买模具,把材料放进去,这样做出来才会达到理想的形状。而对应的模板形参就是所谓的材料。
3.函数模版的实例化
用不同类型的参数使用函数模板时,称为函数模板的实例化。模板参数实例化分为:隐式实例化和显式实例化。
3.1隐式实例化
让编译器根据实参推演模板参数实际类型
template<class T>
T Add(const T& left, const T& right)
{
return left + right;
}
int main()
{
int a1 = 10, a2 = 20;
double d1 = 10.0, d2 = 20.0;
Add(a1, a2);
Add(d1, d2);
}
第一次调用的T被推演为int类型
第二次调用的T被推演为double
但是不可以写出下面这种
Add(a1, d1);
上述语句不能通过编译,因为在编译期间,当编译器看到该实例化时,需要推演其实参类型
通过实参a1将T推演为int,通过实参d1将T推演为double类型,但模板参数列表中只有一个T,编译器无法确定此处到底该将T确定为int 或者 double类型而报错!!!
注意:在模板中,编译器一般不会进行类型转换操作,因为一旦转化出问题,编译器就背大锅!
此时有两种处理方式:1. 用户自己来强制转化 2. 使用显式实例化
3.2显示实例化
在函数名后的<>中指定模板参数的类型
int main(void)
{
int a = 10;
double b = 20.0;
// 显式实例化
Add<int>(a, b);
return 0;
}
如果类型不匹配编译器会尝试进行隐式类型转换
若无法转换成功编译器将会报错
4. 函数模板参数的匹配规则
一个非模板函数可以和一个同名的函数模板同时存在,而且该函数模板还可以被实例化为这个非模板函数
// 专门处理int的加法函数
int Add(int left, int right)
{
return left + right;
}
// 通用加法函数
template<class T>
T Add(T left, T right)
{
return left + right;
}
在调用函数时若参数和非模板函数匹配,那么编译器会优先调用非模板函数
若非模板函数不匹配或模板函数更匹配,那么编译器会优先调用模板函数
5.类模版
template<class T1, class T2, ..., class Tn>
class 类模板名
{
// 类内成员定义
};
实现一个简易的类模版
5.1类模版的实例化
类模板实例化与函数模板实例化不同,类模板实例化需要在类模板名字后跟<>,然后将实例化的类型放在<>中即可,类模板名字不是真正的类,而实例化的结果才是真正的类
注意:
Vector是类名
Vector< int >才是类型
6.总结
泛型编程是C++的一大利器
它极大的减少了代码的复杂程度
并且增加了代码的可读性