函数模板和类模板
1、介绍
1)C++提供两种模板机制:函数模板、类模板
2)作用:使类型参数化,又称参数模板
使得程序(算法)可以从逻辑功能上抽象,将数据类型作为参数传递。
总结:
- 模板把函数或类要处理的数据类型参数化,表现为参数的多态性,称为类属。
- 模板用于表达逻辑结构相同,但具体数据元素类型不同的数据对象的通用行为。
所谓函数模板,实际上是建立一个通用函数,其函数类型和形参类型不具体指定,用一个虚拟的类型来代表。这个通用函数就称为函数模板。
凡是函数体相同的函数都可以用这个模板来代替,不必定义多个函数,只需在模板中定义一次即可。
在调用函数时系统会根据实参的类型来取代模板中的虚拟类型,从而实现了不同函数的功能。
2、函数模板
1)应用场景:函数的业务逻辑一样,但函数的参数类型不一样
函数模板的本质:类型参数化
2)语法
函数模板声明形式 : template < 类型形式参数表 > 类型形式参数的形式为: typename T1 , typename T2 , …… , typename Tn 或 class T1 , class T2 , …… , class Tn 函数模板定义:
template < 类型形式参数表 > 类型 函数名 (形式参数表){ 语句序列 }
3)举例
#includeusing namespace std;//template 关键字告诉C++编译器 我要开始泛型了.你不要随便报错 //数据类型T 参数化数据类型template void myswap(T &a, T &b){ T t; t = a; a = b; b = t;}void main(){ //char a = 'c'; int x = 1; int y = 2; myswap(x, y); //自动数据类型 推导的方式 (不常用,不太好用,看懂即可不推荐使用) float a = 2.0; float b = 3.0; myswap(a, b); //自动数据类型 推导的方式 myswap (a, b); //显示类型调用 cout<<"hello..."<
4)注意
每个模板函数前面都要加函数模板声明:template<class T>
模板函数的调用方式:
sortArray(a, num); //显示类型调用 模板函数 <>
3、函数模板遇上函数重载
函数模板和普通函数在一起,调用规则:
1 模板函数可以像普通函数一样被重载
2 C++编译器优先考虑普通函数
3 如果函数模板可以产生一个更好的匹配,那么选择模板
4 可以通过空模板实参列表的语法限定编译器只通过模板匹配
函数模板和普通函数的本质区别结论:
1、函数模板不允许自动类型转化
2、普通函数能够进行隐式的自动类型转换
举例:
#include "iostream"using namespace std;int Max(int a, int b){ cout<<"int Max(int a, int b)"<b ? a : b;}template T Max(T a, T b){ cout<<"T Max(T a, T b)"< b ? a : b;}template T Max(T a, T b, T c){ cout<<"T Max(T a, T b, T c)"< (a, b)< 类型列表 cout<
结论: 函数模板不提供隐式的数据类型转换,必须是严格的匹配,而普通函数可以隐式转换数据类型。
4、函数模板小结
模板函数进行编译时,编译器会对函数模板进行两次编译,在声明的地方对模板代码本身进行编译;在调用的地方对参数替换后的代码进行编译。
编译器并不是把函数模板处理成能够处理任意类型的函数,编译器从函数模板通过具体类型产生不同的函数。
我们使用函数模板,根据具体类型的参数化,就能适用于不同类型的变量交换,达到了代码复用的效果。
下边来深入理解下函数模板:
- 对于函数模板中使用的类型不同,编译器会产生不同的函数
- 编译器会对函数模板进行两次编译
- 第一次是对函数模板本身进行编译,包括语法检查等(生成函数体)
- 第二次是对参数替换后的代码进行编译,这就相当于编译普通函数一样,进行类型规则检查等。(根据类型生成函数头)
需要注意的是
- 函数模板是不允许隐式类型转换的,调用时类型必须严格匹配
5、类模板
类模板与函数模板的定义和使用类似,当有两个或多个类,其功能是相同的,仅仅是数据类型不同,
如下面语句声明了一个类:
- 类模板用于实现类所需数据的类型参数化
- 类模板在表示如数组、表、图等数据结构显得特别重要,
这些数据结构的表示和算法不受所包含的元素类型的影响
举例:
//类的类型参数化 抽象的类//单个类模板templateclass A {public: A(T t) { this->t = t; } T &getT() { return t; }protected:public: T t;};void main(){ //模板中如果使用了构造函数,则遵守以前的类的构造函数的调用规则 A a(100); a.getT(); printAA(a); return ;}
6、继承中的类模板语法
示例:
//结论: 子类从模板类继承的时候,需要让编译器知道 父类的数据类型具体是什么(数据类型的本质:固定大小内存块的别名)A //class B : public A //(需要调用父类的构造函数,所以用参数列表来做){public: B(int i) : A (i) { } void printB() { cout<<"A:"<<