我们如果要写一个通用的加法函数,那么会有哪些方法实现呢?
1.函数重载
int Add(int left, int right)
{
return left + right;
}
float Add(float left, float right)
{
return left + right;
}
[缺点]
①要是有新的类型出现,就需要添加一个新的函数。函数重载的函数体相同,代码复用率不高,。要是一个函数体出错,其他的都得改;
②如果只是函数类型不同,参数列表相同,不能实现。
2.使用公共基类, 将通用的代码放在公共的基础类里面,让需要这部分功能的类去继承它。
[缺点]
①失去类型检查的优点;
②继承使代码更加难维护。
3.使用特殊的预处理
#define Add() ((x)+(y))
[缺点]
宏不是函数,不可以进行类型检测,安全性不高。
泛型编程:
编写与类型无关的逻辑代码,是代码复用的一种手段,模板是泛型编程的基础。
模板:
通俗来讲,就是依葫芦画瓢的代码生成器。(关键字:template)
函数模板:
实际上是建立一个通用函数,这个函数与类型无关,用模糊类型来代表。
注:typename和class是可以定义模板的,但要注意struct是不可以定义的。
模板函数编译两次:
①实例化之前,进行简单的语法检测(一些可以检测出来,一些检测不出来);
②实例化期间,检查模板代码,查看是否所有的调用有效。
实例化:
实例化是指编译器用模板产生指定的类或者函数的特定类型版本,产生模板特定类型的过程。
隐式实例化:
Add(1,2) Add(1.0f,2.0f)
显式实例化:Add<>(1,2) Add<int>(1,2) Add<char>(1,'2') Add<int>(1,'2')
注:模板函数不是真正意义上的函数。
实参推演
从函数实参确定模板形参类型和值。
模板参数
注意:
1.模板形参名字只能在形参之后模板声明或定义的末尾之间使用,遵循名字屏蔽规则;
2.模板形参名字在同一模板形参中只能使用一次;
3.在函数模板的内部不能指定缺省的模板实参。
类型形参转换的两种情景(隐式实例化可转化的特例)
一般情况下,不会转换形参来匹配已有的实例化,会产生新的实例。
①接收const的引用或指针的函数用非const对象引用或指针调用。
②数组或函数类型转化为指针
非类型形参
模板内部定义常量的时候可以使用非类型形参
1.
2.
template <class T>
void FunTest(T arr,int size)
{
for (int i = 0; i < size; ++i)
{
arr[i] = 0;
}
}
模板函数重载
举个例子:
总结:
1.对于非模板函数和同名模板函数,要是其他条件相同,会优先调用非模板函数;
2.如果模板可以产生一个更好的匹配函数,则模板会产生一个实例;
3.若是显示给出一个空的模板函数列表,编译器会自动调用模板,所有的模板参数会根据实参演绎出来;
4.模板函数不能进行自动类型转化,而普通函数可以继续类型转化。
模板函数特化
看个例子:
这种情况就需要对模板进行特化
template<class T>
int mymax(T t1,T t2)
{
return strcmp(t1, t2);
}
template<>
int mymax<const char*>(const char* const t1, const char* const t2)
{
return strcmp(t1, t2);
}
int main()
{
const char* p1 = "123";
const char* p2 = "abc";
cout<<mymax(p1, p2)<<endl;
system("pause:");
return 0;
}
模板类其实原理和模板函数类似,下节中我们会介绍到。