博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Hello,C++(7)函数模板和类模板
阅读量:6817 次
发布时间:2019-06-26

本文共 4810 字,大约阅读时间需要 16 分钟。

函数模板和类模板

1、介绍

1)C++提供两种模板机制:函数模板、类模板

2)作用:使类型参数化,又称参数模板

使得程序(算法)可以从逻辑功能上抽象,将数据类型作为参数传递

总结:

  • 模板把函数或类要处理的数据类型参数化,表现为参数的多态性,称为类属。
  • 模板用于表达逻辑结构相同,但具体数据元素类型不同的数据对象的通用行为。

所谓函数模板,实际上是建立一个通用函数,其函数类型和形参类型不具体指定,用一个虚拟的类型来代表。这个通用函数就称为函数模板。

凡是函数体相同的函数都可以用这个模板来代替,不必定义多个函数,只需在模板中定义一次即可。

在调用函数时系统会根据实参的类型来取代模板中的虚拟类型,从而实现了不同函数的功能。

2、函数模板

1)应用场景:函数的业务逻辑一样,但函数的参数类型不一样

  函数模板的本质:类型参数化

2)语法

函数模板声明形式 : template    < 类型形式参数表 >    类型形式参数的形式为:            typename T1 ,  typename T2 , …… , typename Tn                 或    class T1 ,  class T2 , …… , class Tn  函数模板定义:
template    < 类型形式参数表 > 类型 函数名 (形式参数表){
  语句序列 }

3)举例

#include 
using 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、类模板

类模板与函数模板的定义和使用类似,当有两个或多个类,其功能是相同的,仅仅是数据类型不同,

如下面语句声明了一个类:

 

  • 类模板用于实现类所需数据的类型参数化
  • 类模板在表示如数组、表、图等数据结构显得特别重要,

 这些数据结构的表示和算法不受所包含的元素类型的影响

举例:

//类的类型参数化 抽象的类//单个类模板template
class 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:"<
<
&a) //类模板做函数参数 { // a.getT();}void main(){ A
a(100); //模板类中如果使用了构造函数,则遵守以前的类的构造函数的调用规则 a.getT(); printAA(a); B b(10); b.printB(); cout<<"hello..."<

类模板有三种方式:

  • 类模板语法知识体系梳理

  • 所有的类模板函数写在类的内部

  • 所有的类模板函数写在类的外部,在一个cpp中

    • 这种方式下:Main文件里要包含.cpp需要把所有.h和.cpp文件放在同一个项目里,main文件要进行两次编译。

尽量用1、3方式。

 

注意三个地方具体化:

1、参数具体化加<T>

2、返回值具体化加<T>

3、函数名 要不要加类的域名作用符

              

 注意:声明友元函数的时候:friend ostream& operator<< <T> (ostream &out, Complex<T> &c3) ;

 

全局函数不要加类的域名作用符,例如operator<<是全局函数

 

友元函数本来就不是类里面的,是全局函数 不需要加域名作用符

 

结论:

友元函数只用来进行左移友移操作符重载。

而且重载<<>>操作符只能用友元函数,其他运算符重载都要用成员函数

不要滥用友元函数

             

8、总结

归纳以上的介绍,可以这样声明和使用类模板:

1) 先写出一个实际的类。由于其语义明确,含义清楚,一般不会出错。2) 将此类中准备改变的类型名(如int要改变为float或char)改用一个自己指定的虚拟类型名(如上例中的numtype)。3) 在类声明前面加入一行,格式为:    template 
如: template
//注意本行末尾无分号 class Compare {…}; //类体4) 用类模板定义对象时用以下形式: 类模板名
<实际类型名>
对象名; 类模板名
<实际类型名>
对象名(实参表列);如: Compare
cmp; Compare
cmp(3,7);5) 如果在类模板外定义成员函数,应写成类模板形式: template
函数类型 类模板名
<虚拟类型参数>
::成员函数名(函数形参表列) {…}

 

9、关于类模板的几点说明:

1) 类模板的类型参数可以有一个或多个,每个类型前面都必须加class,如:

    template <class T1,class T2>

    class someclass

    {…};

在定义对象时分别代入实际的类型名,如:

    someclass<int,double> obj;

2) 和使用类一样,使用类模板时要注意其作用域,只能在其有效作用域内用它定义对象。

3) 类模板可继承

 

10、类模板中的static关键字

  • 从类模板实例化的每个模板类有自己的类模板数据成员,该模板类的所有对象共享一个static数据成员
  •  和非模板类的static数据成员一样,模板类的static数据成员也应该在文件范围定义和初始化
  •  每个模板类有自己的类模板的static数据成员副本

示例:

template
class TA{public: static T a;public: TA() { }};template
T TA
::a = 10;int main(){ //说明static属于的是具体类型的类 TA
Oa; Oa.a += 10; cout << Oa.a << endl; TA
Ob; cout << Ob.a << endl; TA
Ca; cout << (int)Ca.a << endl; return 0;}

 

结论:

static属于类模板实例化的模板类。实例化的模板类有多个,则static成员分别属于多个模板类。即模板类有自己的static成员,互不干扰。

转载于:https://www.cnblogs.com/juanjuanduang/p/10864059.html

你可能感兴趣的文章
React 源码剖析系列 - 不可思议的 react diff
查看>>
走近抽象类与抽象方法
查看>>
4. 寻找两个有序数组的中位数
查看>>
React组件开发总结
查看>>
各种符号
查看>>
大道至简,职场上做人做事做管理
查看>>
抗干扰的秘诀:分类、整理与专注
查看>>
Number of Connected Components in an Undirected Graph
查看>>
BZOJ 3143 游走(高斯消元)
查看>>
SpringBoot 配置文件存放位置及读取顺序
查看>>
min.js格式化工具
查看>>
《软件工程-理论、方法与实践》读书笔记一
查看>>
POJ Problem Radar Installation 【贪心】
查看>>
redis 持久化方式
查看>>
Vue2.0设置反向代理解决跨域问题
查看>>
伪类link,hover,active,visited,focus的区别
查看>>
WTL 实践笔记
查看>>
漫谈C++:良好的编程习惯与编程要点(转载)
查看>>
Jquery plugin ScrollUp使用和实现
查看>>
使用HTML5 FormData对象实现大文件分块上传(断点上传)功能
查看>>