一:函数模板

建立一个通用函数,其函数类型和形参类型不具体指定,用一个虚拟的类型来代表。这个通用函数就称为函数模板。
凡是函数体相同的函数都可以用这个模板来代替,不必定义多个函数,只需要在模板中定义一次即可

(一)函数模板语法

template <类型形式参数表>
类型 函数名(形参参数表)
{
执行语句;
}
其中类型形式参数表形式为:
typename T1,typename T2,...,typename Tn
或者
class T1,class T2,...,class Tn

(二)函数模板和普通函数相比

(1)参数转换问题

普通函数可以进行隐式函数类型转换
模板函数不允许存在这种转换,类型严格一致

(2)调用规则问题

.函数模板可以像普通函数一样被重载
.C++编译器优先考虑普通函数
.如果函数模板可以产生一个更好的匹配,那么选择模板
.可以通过空模板实参列表的语法限定编译器只通过模板匹配

(三)函数模板的使用

template <typename T1,typename T2>    //参数列表为2个
void sortArray(T1* arr, T2 num)
{
T2 i, j;
T1 temp;
for (i = ; i < num; i++)
for (j = ; j < num; j++)
if (arr[i]>arr[j])
{
temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
} template <typename T>
void printArr(T* arr, int num) //参数列表可以含有固定类型参数 返回值类型可以自定义,也可以为typename定义类型

{
int i;
for (i = ; i < num; i++)
{
cout << arr[i]<<" ";
}
cout << endl;
}
void main()
{
//测试int类型
int a[] = { , , , , };
sortArray(a, );
printArr(a, ); //测试float类型
float fa[] = { 2.6, 37.1, 62.9, 16.2, };
sortArray(fa, );
printArr(fa, ); system("pause");
}

(四)当函数模板遇到函数重载《重点》

1.函数模板可以像普通函数一样被重载

//实现参数交换
template<typename T>
void mySwap(T& t1, T& t2)
{
T temp;
temp = t1;
t1 = t2;
t2 = temp;
} void mySwap(char &b, int& a)
{
int temp;
temp = a;
a = b;
b = temp;
} void main()
{
char a = 'A';
int b = ;
mySwap(a, b); cout << "a:" << a<<endl;
cout << "b:" << b << endl; system("pause");
}
由于模板函数参数类型严格定义,故会去找下面的进行调用。但是不难看出是允许函数模板重载的

2.C++编译器优先考虑普通函数

//实现参数交换
template<typename T>
void mySwap(T& t1, T& t2)
{
T temp;
temp = t1;
t1 = t2;
t2 = temp;
cout << "template func" << endl;
} void mySwap(int &b, int& a)
{
int temp;
temp = a;
a = b;
b = temp;
cout << "ordinary func" << endl;
} void main()
{
int a = , b = ;
mySwap(a, b); cout << "a:" << a<<endl;
cout << "b:" << b << endl; system("pause");
}
可以看出模板函数和普通函数都是可以匹配成功的,但是C++编译器却优先选取了普通函数

3.如果函数模板可以产生一个更好的匹配,那么选择模板

//实现参数交换
template<typename T>
void mySwap(T& t1, T& t2)
{
T temp;
temp = t1;
t1 = t2;
t2 = temp;
cout << "template func" << endl;
} void mySwap(int &b, int& a)
{
int temp;
temp = a;
a = b;
b = temp;
cout << "ordinary func" << endl;
} void main()
{
float a = 16.5, b = 64.6;
mySwap(a, b); cout << "a:" << a<<endl;
cout << "b:" << b << endl; system("pause");
}
可以看出,在符合模板函数参数列表严格匹配的条件下,若是和模板函数更好匹配,那么会去选择模板函数

4.可以通过空模板实参列表的语法限定编译器只通过模板匹配

template<typename T>
void mySwap(T& t1, T& t2)
{
T temp;
temp = t1;
t1 = t2;
t2 = temp;
cout << "template func" << endl;
} void mySwap(int &b, int& a)
{
int temp;
temp = a;
a = b;
b = temp;
cout << "ordinary func" << endl;
} void main()
{
int a = , b = ;
mySwap(a, b); //若是不做处理会优先调用普通函数 //下面通过<>类型列表,显示调用模板函数
mySwap<>(a, b); cout << "a:" << a<<endl;
cout << "b:" << b << endl; system("pause");
}
通过空模板<>列表可以显示调用模板函数

(五)总结

.编译器并不是把函数模板处理成能够处理任意类的函数
.编译器从函数模板通过具体类型产生不同的函数
.编译器会对函数模板进行两次编译:
在声明的地方对函数模板进行编译
在调用的地方对参数替换后的代码进行编译

二:类模板

(一)单个类模板语法

//也可以通过类属参数进行直接传参
template<typename T,int exp1,typename T3> //类属参数列表写了,可以不用(不会报错),但是既然不用,写他也就没有意义

class A
{
public:
T t; //注意:类属参数列表中的参数,至少在类说明中出现一次
public:
A(int a)
{
this->t = a + exp1;
} void getInfo()
{
cout <<"getInfo:"<<this->t << endl;
}
}; void main()
{
A<int,10,int> a(5); //模板类调用
a.getInfo(); system("pause");
}

(二)继承中的函数模板:派生类必须实例化基类(指定好参数列表)

template<typename T>
class A
{
public:
T t;
public:
A(T a)
{
this->t = a ;
} void getInfo()
{
cout <<"A getInfo:"<<this->t << endl;
}
}; class B :public A<float>
{
public:
int b;
public:
B(int n, float m) :A<float>(m)
{
b = n;
} void getInfo()
{
cout << "B getInfo:" << this->b << endl;;
A<float>::getInfo();
}
}; void main()
{
B b(, 6.5);
b.getInfo(); system("pause");
}

总之:子类从模板类继承时,一定要让编译器知道父类的具体类型,才可以进行内存分配。

(三) 类模板方法在类外部进行定义(上面都是在内部定义)

template <class 虚拟类型参数>
函数类型 类模板名<虚拟类型参数>::成员函数名(函数参数列表)
{
....
}
template<typename T>
class A
{
public:
T t;
public:
A(T a)
{
this->t = a ;
} T getInfo();  //需要提前声明
}; template<class T>  //typename也可以
T A<T>
::getInfo()
{
cout << "A outfunc" << endl;
return this->t;
} void main()
{
int ret;
A<int> a();
ret=a.getInfo();
cout << "ret:" << ret << endl;
system("pause");
}

(四)类模板友元函数使用(声明,实现,调用3个都要注意)

template<typename T>
class A
{
public:
T t;
public:
A(T a)
{
this->t = a ;
} T getInfo(); friend A<T> mySub(A<T> &a1, A<T> &a2);  //1.声明
}; template <typename T>               //2.实现
A<T> mySub(A<T> &a1, A<T> &a2)
{
A<T> a(a1.t - a2.t);
return
a;
}
template<typename T>
T A<T>::getInfo()
{
cout << "A outfunc" << endl;
return this->t;
} void main()
{
int ret;
A<int> a();
A<int> b(); //注意友元函数的调用方法
A<int> c = mySub<int>(a, b);      //3.调用 //返回匿名对象,直接初始化给c ret=c.getInfo();
cout << "ret:" << ret << endl;
system("pause");
}

C++回顾day03---<模板>的更多相关文章

  1. Java I/O(二)其他常用的输入输出流PrintStream等、标准流重定向

    四.FilterOutputStream.PrintStream PrintStream是继承自FilterStream类的,例如标准输出流System.out就是著名的PrintStream类对象. ...

  2. java.IO输入输出流:过滤流:buffer流和data流

    java.io使用了适配器模式装饰模式等设计模式来解决字符流的套接和输入输出问题. 字节流只能一次处理一个字节,为了更方便的操作数据,便加入了套接流. 问题引入:缓冲流为什么比普通的文件字节流效率高? ...

  3. Java中IO流,输入输出流概述与总结

    总结的很粗糙,以后时间富裕了好好修改一下. 1:Java语言定义了许多类专门负责各种方式的输入或者输出,这些类都被放在java.io包中.其中, 所有输入流类都是抽象类InputStream(字节输入 ...

  4. 第27章 java I/O输入输出流

    java I/O输入输出流 1.编码问题 import java.io.UnsupportedEncodingException; /** * java涉及的编码 */ public class En ...

  5. java 对象输入输出流

    对象的输入输出流的作用: 用于写入对象 的信息读取对象的信息. 对象的持久化. 比如:用户信息.              ObjectInputStream   : 对象输入流            ...

  6. 【转】输入/输出流 - 全面掌握IO

    File类: 程序中操作文件和目录都可以使用File类来完成即不管是文件还是目录都是使用File类来操作的,File能新建,删除,重命名文件和目录,但File不能访问文件内容本身,如果需要访问文件本身 ...

  7. 输入输出流(IO)

    输入输出流(IO)文件(File)java.io.File用于表示文件(目录),也就是说程序员可以通过File类在程序中操作硬盘上的文件和目录.File类只用于表示文件(目录)的信息(名称.大小等), ...

  8. Java输入/输出流体系

    在用java的io流读写文件时,总是被它的各种流能得很混乱,有40多个类,理清啦,过一段时间又混乱啦,决定整理一下!以防再忘 Java输入/输出流体系 1.字节流和字符流 字节流:按字节读取.字符流: ...

  9. JAVA Io 缓冲输入输出流

    java中提供带缓冲的输入输出流.在打开文件进行写入或读取操作时,都会加上缓冲,提高了IO读写性能. 1. BufferedInputStream 缓冲输入流 2. BufferedOutputStr ...

  10. C++输入输出流

    一.C++输入输出流的含义 以前所用到的输入和输出,都是以终端为对象的,即从键盘输入数据,运行结果输出到显示器屏幕上.从操作系统的角度看,每一个与主机相连的输入输出设备都被看作一个文件.程序的输入指的 ...

随机推荐

  1. IOS开发protocol使用

    1.什么是protocol? protocol(协议)是用来定义对象的属性和行为,用于回调. 2.protocol分类? 协议中有三个修饰关键字@required和@optional和@propert ...

  2. STP生成树协议

    STP主要作用 1.消除环路:通过阻断冗余链路来消除网络中可能存在的链路 2.链路备份:当活动那个路径发生故障时,激活备份链路,及时恢复网络连通性. 根桥选举 每个交换机启动STP后,都认为自己是根桥 ...

  3. 浪潮服务器I4008/NX5480M4介绍

    浪潮I4008 / NX5480M4是一款高密度模块化服务器. I4008是机箱,NX5480M4是节点. 8个计算节点模块可以部署在标准机架4U高度机器里,具有高性能.低功耗.易维护.组管理功能.适 ...

  4. 在Linux系统中同步更新我们的Github博客

    原理介绍 类似于版本管理,我们把我们的hexo博客文件系统在Github上建立一个分支,通过管理分支提交最新的博客文件系统,保证我们博客框架的更新.然后我们基于最新的博客框架,撰写文章,进行Githu ...

  5. 解决topjui中工具栏按钮删除刷新从属表

    遇到了这么个问题:当在从属datagrid表格中,点击主表工具栏按钮中的删除,通过后台的多表删除的sql,返回给前台之后,从属表的数据成功在数据库中删除,但是在前台页面显示的时候,只刷新了主表,子表未 ...

  6. 【Python使用】使用pip安装卸载Python包(含离线安装Python包)未完成???

    pip 是 Python 包管理工具,该工具提供了对Python包的查找.下载.安装.卸载的功能.Python 2.7.9 + 或 Python 3.4+ 以上版本都自带 pip 工具. pip使用( ...

  7. git、github、gitlab之间的关系

    GIt-版本控制工具:GitHub-一个网站平台,提供给用户空间存储git仓储,保存用户的一些数据文档或者代码等:GitLab - 基于Git的项目管理软件. Git分布式版本控制系统 Git是一款自 ...

  8. How Cigna Tuned Its Spark Streaming App for Real-time Processing with Apache Kafka

    Explore the configuration changes that Cigna’s Big Data Analytics team has made to optimize the perf ...

  9. SpringBoot学习笔记(2) Spring Boot的一些配置

    外部配置 Spring Boot允许使用properties文件.yaml文件或者命令行参数作为外部配置 使用@Value注解,可以直接将属性值注入到你的beans中,并通过Spring的Enviro ...

  10. SDOI2019R1游记

    差点退役,真是开心 Day -2 吐了一晚上,差点死掉 被拉去医院打针,结果蛇皮的被扎了两针,真是好疼啊嘤嘤嘤 决定第二天在家里咕一天 Day -1 结果在家里也得做\(loli\)昨天的不知道从哪里 ...