C++函数模板(模板函数)详解
定义
函数模板不是⼀个实在的函数,编译器不能为其⽣成可执⾏代码。定义函数模板后只是⼀个对函数功能框架的描述,当它具体执⾏时,将根据传递的实际参数决定其功能。(好吧,咱也听不懂,直接上⽤法吧?)
⽤法:
⾯向对象的继承和多态机制有效提⾼了程序的可重⽤性和可扩充性。在程序的可重⽤性⽅⾯,程序员还希望得到更多⽀持。举⼀个最简单的例⼦,为了交换两个整型变量的值,需要写下⾯的 Swap 函数:
void Swap(int & x, int & y)
{
int tmp = x;
奔驰cclassx = y;
y = tmp;
}
为了交换两个 double 型变量的值,还需要编写下⾯的 Swap 函数:
void Swap (double & xr double & y)
{
double tmp = x;
x = y;
y = tmp;
}
如果还要交换两个 char 型变量的值,交换两个 CStudent 类对象的值……都需要再编写 Swap 函数。⽽这些 Swap 函数除了处理的数据类型不同外,形式上都是⼀样的。能否只写⼀遍 Swap 函数,就能⽤来交换各种类型的变量的值呢?继承和多态显然⽆法解决这个问题。因此,“模板”的概念就应运⽽⽣
了。
众所周知,有了“模⼦”后,⽤“模⼦”来批量制造陶瓷、塑料、⾦属制品等就变得容易了。程序设计语⾔中的模板就是⽤来批量⽣成功能和形式都⼏乎相同的代码的。有了模板,编译器就能在需要的时候,根据模板⾃动⽣成程序的代码。从同⼀个模板⾃动⽣成的代码,形式⼏乎是⼀样的。汽车漂移教程
函数模板的原理
C++ 语⾔⽀持模板。有了模板,可以只写⼀个 Swap 模板,编译器会根据 Swap 模板⾃动⽣成多个 Sawp 函数,⽤以交换不同类型变量的值。
在 C++ 中,模板分为函数模板和类模板两种。
函数模板是⽤于⽣成函数;
类模板则是⽤于⽣成类的。
函数模板的写法如下:
template <class 类型参数1, class类型参数2, ...>
返回值类型模板名(形参表)
{
函数体
}
其中的 class 关键字也可以⽤ typename 关键字替换,例如:拉古娜
template <typename 类型参数1, typename 类型参数2, ...>
函数模板看上去就像⼀个函数。前⾯提到的 Swap 模板的写法如下:
template <class T>
void Swap(T & x, T & y)
{
T tmp = x;
x = y;
天津雾霾限号y = tmp;
}
T 是类型参数,代表类型。编译器由模板⾃动⽣成函数时,会⽤具体的类型名对模板中所有的类型参数进⾏替换,其他部分则原封不动地保留。同⼀个类型参数只能替换为同⼀种类型。编译器在编译到调⽤函数模板的语句时,会根据实参的类型判断该如何替换模板中的类型参数。
例如下⾯的程序:
#include <iostream>
using namespace std;
template<class T>
void Swap(T & x, T & y)
{
T tmp = x;
x = y;
y = tmp;
}
int main()
{
int n = 1, m = 2;
Swap(n, m);  //编译器⾃动⽣成 void Swap (int &, int &)函数
double f = 1.2, g = 2.3;
Swap(f, g);  //编译器⾃动⽣成 void Swap (double &, double &)函数
return 0;
}
编译器在编译到Swap(n, m);时不到函数 Swap 的定义,但是发现实参 n、m 都是 int 类型的,⽤ int 类型替换 Swap 模板中的 T 能得到下⾯的函数:
void Swap (int & x, int & y)
{
int tmp = x;
x = y;
y = tmp;
}
该函数可以匹配Swap(n, m);这条语句。于是编译器就⾃动⽤ int 替换 Swap 模板中的 T,⽣成上⾯的 Swap 函数,将该 Swap 函数的源代码加⼊程序中⼀起编译,并且将Swap(n, m);编译成对⾃动⽣成的 Swap 函数的调⽤。
同理,编译器在编译到Swap(f, g);时会⽤ double 替换 Swap 模板中的 T,⾃动⽣成以下 Swap 函数:
void Swap(double & x, double & y)
{
double tmp = x;
x = y;
y = tmp;
}
然后再将Swap(f, g);编译成对该 Swap 函数的调⽤。
编译器由模板⾃动⽣成函数的过程叫模板的实例化。由模板实例化⽽得到的函数称为模板函数。在某些编译器中,模板只有在被实例化时,编译器才会检查其语法正确性。如果程序中写了⼀个模板却没有⽤到,那么编译器不会报告这个模板中的语法错误。
编译器对模板进⾏实例化时,并⾮只能通过模板调⽤语句的实参来实例化模板中的类型参数,模板调⽤语句可以明确指明要把类型参数实例化为哪种类型。可以⽤:
模板名<;实际类型参数1,实际类型参数2, ...>
的⽅式告诉编译器应该如何实例化模板函数。例如下⾯的程序:
#include <iostream>
using namespace std;
template <class T>
T Inc(int n)
{
return 1 + n;
}
int main()
{
cout << Inc<double>(4) / 2;
return 0;
}
Inc(4)指明了此处实例化的模板函数原型应为:
double Inc(double);
编译器不会因为实参 4 是 int 类型,就⽣成原型为 int Inc(int) 的函数。因此,上⾯程序输出的结果是 2.5 ⽽⾮ 2。
函数模板中可以有不⽌⼀个类型参数。例如,下⾯这个函数模板的写法是合法的:
template <class Tl, class T2>
T2 print(T1 argl, T2 arg2)
{
cout << arg1 << " " << arg2 << endl;
return arg2;
}
【实例】⼀个求数组中最⼤元素的函数模板
例题:设计⼀个分数类 CFraction,再设计⼀个名为 MaxElement 的函数模板,能够求数组中最⼤的元素,并⽤该模板求⼀个 CFmction 数组中的最⼤元素。
⽰例程序如下:
华普怎么样#include <iostream>
dvd播放格式
using namespace std;
template <class T>
T MaxElement(T a[], int size) //size是数组元素个数
{
T tmpMax = a[0];
for (int i = 1; i < size; ++i)
if (tmpMax < a[i])
tmpMax = a[i];
return tmpMax;
}
class CFraction //分数类
{
int numerator;  //分⼦
int denominator; //分母
public:
CFraction(int n, int d) :numerator(n), denominator(d){};
bool operator <(const CFraction & f) const
{//为避免除法产⽣的浮点误差,⽤乘法判断两个分数的⼤⼩关系
if (denominator * f.denominator > 0)
return numerator * f.denominator < denominator * f.numerator;
else
return numerator * f.denominator > denominator * f.numerator;
}
bool operator == (const CFraction & f) const
{//为避免除法产⽣的浮点误差,⽤乘法判断两个分数是否相等
return numerator * f.denominator == denominator * f.numerator;
}
friend ostream & operator <<(ostream & o, const CFraction & f);
};
ostream & operator <<(ostream & o, const CFraction & f)
{//重载 << 使得分数对象可以通过cout输出
o << f.numerator << "/" << f.denominator; //输出"分⼦/分母"形式
return o;
}
int main()
{
int a[5] ={ 1,5,2,3,4 };
CFraction f[4] ={CFraction(8,6),CFraction(-8,4),
CFraction(3,2),CFraction(5,6)};
cout << MaxElement(a, 5) << endl;
cout << MaxElement(f, 4) << endl;
return 0;
}
编译到第 41 ⾏时,根据实参 a 的类型,编译器通过 MaxElement 模板⾃动⽣成了⼀个 MaxElement 函数,原型为:
int MaxElement(int a[], int size);
编译到第 42 ⾏时,根据 f 的类型,编译器⼜⽣成⼀个 MaxElement 函数,原型为:
CFraction MaxElement(CFraction a[], int size);
在该函数中,⽤到了<⽐较两个 CFraction 对象的⼤⼩。如果没有对<;进⾏适当的重载,编译时就会出错。
从 MaxElement 模板的写法可以看出,在函数模板中,类型参数不但可以⽤来定义参数的类型,还能⽤于定义局部变量和函数模板的返回值。
延申⽤法
2.1为什么需要类模板
类模板与函数模板的定义和使⽤类似,我们已经进⾏了介绍。 有时,有两个或多个类,其功能是相同的,仅仅是数据类型不同,如下⾯语句声明了⼀个类:
类模板⽤于实现类所需数据的类型参数化
类模板在表⽰如数组、表、图等数据结构显得特别重要,
这些数据结构的表⽰和算法不受所包含的元素类型的影响
2.2单个类模板语法
1 //类的类型参数化抽象的类
2 //单个类模板
3 template<typename T>
4 class A
5{
6 public:
7    A(T t)
8{
9        this->t = t;
10    }
11
12    T &getT()
13{
14        return t;
15    }
16 protected:
17 public:
18    T t;
19 };
20 void main()
21{
22    //模板了中如果使⽤了构造函数,则遵守以前的类的构造函数的调⽤规则
23    A<int>  a(100);
24    a.getT();
25    printAA(a);
26    return ;
27 }
2.3继承中的类模板语法