1. 使用函数重载
【缺陷】
只要有新类型出现,就要重新添加对应函数
除类型外,所有函数的函数体都相同,代码的复用率不高
如果函数只是返回值类型不同,函数重载不能解决
一个方法有问题,所有的方法都有问题,不好维护
2. 使用公共基类
【缺陷】
-借助公共基类来编写通用代码,将失去类型检查的优点
-对于以后实现的许多类,都必须继承自某个特定的基类,代码维护更加困难
3. 使用宏函数
【缺陷】
不是函数,不进行类型检测,安全性不高,表达式复杂容易出错
那还有什么其他方式吗? 能否将写代码的规则告诉编译器,让编译器来实现呢?、
4. 泛型编程
编写与类型无关的逻辑代码,是代码复用的一种手段。模板是泛型编程的基础
函数模板
1. 概念 函数模板代表了一个函数家族,该函数模板与类型无关,在使用时被参数化,根据实参类型产生函数的特定类型版本2. 函数模板格式
template </typename t1, typename t2,......,class tn>
返回值类型 函数名(参数列表(参数如果有多个,每一个前都要加 typename 或 class, 用逗号隔开)){}
template<typename T>
T Add(const T& left, const T& right)
{
return left + right;
}
注意:typename是用来定义模板参数关键字,也可以使用class(切记:不能使用struct代替typename)建议尽量使用typename
函数模板还可以被定义为inline类型的:
template<class T>
inline T Add(const T& left, const T& right)
{
return left + right;
}
注意:inline关键字必须放在模板形参表之后,返回值之前,不能放在template之前。
3. 函数模板实例化&参数推演
模板是一个蓝图,它本身不是类或者函数,编译器用模板产生指定的类或者函数的特定类型版本,产生模板特定类型的过程称为函数模板实例化
#include<iostream>
using namespace std;
template<typename T>//相当于声明了 T 这样一个类型
inline T Add(const T& left, const T& right)
{
cout << typeid(left).name() << endl;
return left + right;
}
int main()
{
//模板函数的隐式实例化(需要根据参数的类型去推演 T 的类型)
Add(1, 2);
Add(1.0, 2.0);
Add('1', '2');
//Add(1, '1');//报错: 模板 T 类型不明确
//模板函数的显式实例化,已经明确指定了 T 的类型
Add<int>(1, '1');//相当于明确指定了模板里面的 T 为 int,会把字符类型'1'转换为 int 类型的 1
return 0;
}
输出结果:
真正意义上的 Add 函数的原型
int ---> Add<int>
double--->Add<double>
char--->Add<char>
注意:模板函数的编译分两个阶段
实例化之前,检查模板代码本身,是否出现简单语法错误,如:模板参数列表少了 typename
在实例化后,检查模板生成的代码,是否所有的调用都有效,如:实例化类型不支持某些函数调用
从函数实参确定模板形参类型和值的过程称为模板实参推演,多个类型形参的实参必须完全匹配