C++基础——模板讲解

news/2024/5/14 21:17:13/文章来源:https://blog.csdn.net/weixin_69283129/article/details/127845086

目录

一. 泛型编程

二. 函数模板

1.格式:

2.定义:

1.隐式实例化

2.显式实例化 

 3.解决方法3:使用多个T类型

4.在C++中编译器允许非模板函数和模板函数同时存在


一. 泛型编程

        先来看一段代码:

void Swap(int& p1, int& p2) {int tmp = p1;p1 = p2;p2 = tmp;
}int main(){int a = 1, b = 2;cout << "a:" << a << "  b:" << b << endl;Swap(a, b);cout << "a:" << a <<"  b:"<<b << endl;return 0;}

        若是我们需要对浮点型数据进行交换函数,还得再去写一个Swap函数 :

void Swap(double& p1, double& p2) {double tmp = p1;p1 = p2;p2 = tmp;
}void Swap(char& p1, char& p2) {char tmp = p1;p1 = p2;p2 = tmp;
}int main() {int a = 1, b = 2;cout << "a:" << a << "  b:" << b << endl;Swap(a, b);cout << "a:" << a <<"  b:"<<b << endl;cout << "——————————————————————————————" << endl;double c = 10.5, d = 20.8;cout << "c:" << c << "  d:" << d << endl;Swap(c, d);cout << "c:" << c << "  d:" << d << endl;cout << "——————————————————————————————" << endl;char e = 'a', f ='h';cout << "e:" << e << "  f:" << f << endl;Swap(e, f);cout << "e:" << e << "  f:" << f << endl;return 0;
}

          这就说明了一个问题:若有多个变量之间的数据交换,就要频繁去写各种类型的函数,函数体大致是一样的,只是参数类型不同,导致其中含有大量重复代码,代码耦合度高,更容易出bug。

        所以我们需要一个模板,模板是一个蓝图,它本身并不是函数,是编译器用使用方式产生特定具体类型函数的模具。所以其实模板就是将本来应该我们做的重复的事情交给了编译器。

二. 函数模板

1.格式:


template<typenameT1,typename T2,......>
返回值类型 函数名(参数列表){}

        class用于定义类,在c++引入模板后,最初定义模板的方法为:template,这里class关键字表明T是一个广泛的类型。后来为了避免class在这两个地方的使用可能给人带来混淆,所以引入了typename这个关键字。它的作用同class一样表明后面的符号为一个类型。这样在定义模板的时候就能够使用以下的方式了: template.在模板定义语法中关键字class与typename的作用全然一样。 T就是泛型——广泛类型,它里面包含内置类型和你所创建的自定义类型。

2.定义:


        一个类模板(也称为类属类或类生成类)同意用户为类定义一种模式。使得类中的某些数据成员、默写成员函数的參数、某些成员函数的返回值,能够取随意类型(包含系统提前定义的和用户自己定义的)。
        假设一个类中数据成员的数据类型不能确定。或者是某个成员函数的參数或返回值的类型不能确定。就必须将此类声明为模板,它的存在不是代表一个详细的、实际的类,而是代表着      (一类)类。

例:        

template <typename T>
void Swap(T& left, T& right) {T tmp = left;left = right;right = tmp;
}
int main() {int a = 10, b = 20;Swap(a, b);cout << "a:" << a << "  b:" << b << endl;double c = 33.3, d = 66.6;Swap(c, d);cout << "c:" << c << "  d:" << d << endl;char e = 'y', f = 'r';Swap(e, f);cout << "e:" << e << "  f:" << f << endl;return 0;
}

        编译器在指向模板函数的调用时,会先进行类型推演,它会把接收到的实参类型推演生成对应类型的函数以供调用。

        例如:当double类型数据被作为实参去调用函数模板时,编译器通过实参的double类型进行推演,将T确定为double类型,然后专门产生一份处理double类型的代码。

        其次需要注意的是:使用函数模板时,其函数会实例化出对象,在上面的代码中,共有三种类型数据进行三次函数调用,那么就会实例化出三个对象。但若是同种类型的多次调用,编译器只会生成一个对象:

int a = 10, b = 20;Swap(a, b);cout << "a:" << a << "  b:" << b << endl;
int x = 500, y = 1220;Swap(x, y);cout << "a:" << a << "  b:" << b << endl;
int r=63,t=79;Swap(r, t);cout << "r:" << r << "  t:" << t << endl;

        如上图,这也是进行了三次模板函数的调用,但只实例化出一个对象,这是因为模板函数对于同种类型的多次调用,也只会实例化出一个对象。

        用不同类型的参数使用函数模板时,称为函数模板的实例化。模板参数实例化分为:隐式实例化和显式实例化。

1.隐式实例化

        看代码:

template<class T>
T Add(const T& l, const T& r) {return l + r;
}int main() {int a1= 1,a2=2;double b = 5.6,b2=7.8;Add(a1,a1);Add(b1,b2);//两种不同类型的数据相加Add(a1,b1);return 0;
}

        前两个Add函数是两个同类型间相加,第三个是int和double类型相加,这不会通过编译,在编译期间编译器看到模板函数调用时,会实例化出对象,需要推演其实参的类型,通过参数一将T推演成int,通过参数二将T推演成double,但模板只有一个T,无法确定T是int还是double,因此报错。注:编译器可不会因为两种类型的不同而进行类型转换操作!!! 

所以解决方法1:就是在编译器推演实参类型前,就把两个不同类型的转换为同一类型

    Add(a, (int)c);Add((double)a,c);

        这种方式称为隐式实例化,第一个Add函数先将变量c转换为int类型,这样就可以进行推演了,T最终会变成int类型;第二个Add函数 将变量a转换为double类型,T最终变成double型。

2.显式实例化 

        在函数名后加一对尖括号,里面填写你最终要转换出的类型,这也是T的最终类型。

int main() {int a = 10;double b = 20.3;Add<int>(a, b);cout << "Add:" <<Add<int>(a,b) << endl;Add<double>(a, b);cout << "Add:" << Add<double>(a, b) << endl;return 0;
}

 3.解决方法3:使用多个T类型

template <class T1, class T2>
T1 Add2(const T1& left, const T2& right) {return left + right;
}int main() {int a1 = 10, a2 = 20;double b1 = 10.1, b2 = 20.2;cout << Add2(a1, a2) << endl;cout << Add2(b1, b2) << endl;cout << Add2(a1, b2) << endl;cout << Add2(b1, a2) << endl;	//最终结果的类型是根据第一个参数类型所决定return 0;
}

        直接写出两个T类型,这样即使不用隐式和显式转换也可以进行不同类型间的数据相加,最终结果的类型可以设置为int,也可以是double。


4.在C++中编译器允许非模板函数和模板函数同时存在

例:

template <class T1, class T2>
T1 Add( T1& left,T2& right) {return left + right;
}int Add(int& left, int& right) {return left + right;
}
int main() {int a = 15, b = 30;double c = 33.3, d = 99.9;Add(a, b);	Add(c, d);return 0;
}

结果: 

        对于非模板函数和同名函数模板,如果其他条件都相同,编译器会优先调用非模板函数,而模板函数可以生成更加匹配的版本。所以第一个Add函数内部的数据都是int,那么编译器肯定会选择现有的int Add函数去调用,其实编译器也很懒的,哈哈哈~。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.luyixian.cn/news_show_224053.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

LeetCode:8. 字符串转换整数 (atoi)

8. 字符串转换整数 &#xff08;atoi&#xff09;1&#xff09;题目2&#xff09;思路3&#xff09;代码4&#xff09;结果1&#xff09;题目 请你来实现一个 myAtoi(string s) 函数&#xff0c;使其能将字符串转换成一个 32 位有符号整数&#xff08;类似 C/C 中的 atoi 函数…

逻辑判断与正则表达式文本处理

上一篇文章分享了正则表达式的操作&#xff0c;这一篇文章就让我们一起看看正则表达式与逻辑判断结合起来会发生什么吧&#xff01;感兴趣的小伙伴欢迎评论区或者是私信留言&#xff01; 一、题目描述&#xff1a; 输入一个字符串,检查其是否为合法的python变量。输入$$$结束:…

数据结构【队列】

文章目录&#xff08;一&#xff09;队列定义&#xff08;二&#xff09;队列实现&#xff08;1&#xff09;创建结构体&#xff08;2&#xff09;具体函数实现及解析1.1 初始化队列1.2入队列1.3出队列1.4取队首元素1.5取队尾元素1.6返回队列个数1.7判断是否为空1.8销毁队列&am…

FITC标记的STAT1-ASON,绿色荧光素标记STAT1反义寡核苷酸,FITC-STAT1-ASON

产品名称&#xff1a;FITC标记的STAT1-ASON&#xff0c;绿色荧光素标记STAT1反义寡核苷酸 ​​​​​​​英文名称&#xff1a;FITC-STAT1-ASON STAT1 是第一个被发现的 STATs 家族成员&#xff0c;其编码基因位于 2 号染色体上&#xff0c;由 750 个氨基酸残基组成&#xff…

随想录一刷Day55——动态规划

文章目录Day55_动态规划47. 判断子序列48. 不同的子序列Day55_动态规划 47. 判断子序列 392. 判断子序列 思路&#xff1a; 双指针很简单&#xff0c;O(n)O(n)O(n) 时间就能解决 这里还是用dp dp[i][j] 表示以 s[i - 1] 结尾的字符串和以 t[]i-1 为结尾的字符串的最大子序列长…

Linux篇【5】:Linux 进程概念(二)

目录 3.5、查看进程 3.6、通过系统调用接口获取正在进行的进程的标识符 3.7、通过系统调用接口创建子进程 - fork 初识 3.5、查看进程 [HJMhjmlcc ~]$ clear [HJMhjmlcc ~]$ pwd /home/HJM [HJMhjmlcc ~]$ ls [HJMhjmlcc ~]$ touch mytest.c [HJMhjmlcc ~]$ ls mytest.c [H…

基于51单片机的简易数字计算器Proteus仿真

资料编号&#xff1a;115 下面是相关功能视频演示&#xff1a; 115-基于51单片机的简易数字计算器Proteus仿真&#xff08;源码仿真全套资料&#xff09;功能说明&#xff1a; 该计算器系统51 系列的单片机进行的数字计算器系统设计&#xff0c;可以完成计算器的键盘输入&…

一看就会的Java方法

文章目录一、方法的定义和使用&#x1f351;1、为什么引入方法&#xff1f;&#x1f351;2、方法的定义&#x1f351;3、方法调用的执行过程&#x1f351;4、实参和形参的关系二、方法重载&#x1f351;1、为什么需要方法重载&#x1f351;2、方法重载的概念和特点&#x1f351…

用DIV+CSS技术设计的体育主题网站(足球介绍)

&#x1f389;精彩专栏推荐 &#x1f4ad;文末获取联系 ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业&#xff1a; 【&#x1f4da;毕设项目精品实战案例 (10…

【面经】之小鼠喝药问题

题目 现在有 10 只小白鼠和 1000 支药水&#xff0c;1000 支药水中有且仅有一支药水有毒&#xff0c;如果小白鼠喝下毒药&#xff0c;那么毒发的时间是两小时。 现在只给你两小时的时间&#xff0c;请问如何用这 10 只小白鼠测出哪支药水有毒&#xff1f;&#xff08;忽略小白…

linux系统文件权限

目录 shell命令以及运行原理 具体体现(命令行解释器) Linux权限的概念 Linux下有两种用户&#xff1a;超级用户&#xff08;root&#xff09;、普通用户 su指令 Linux权限管理方面 文件访问者的分类&#xff08;人&#xff09; 为什么要有所属组&#xff1f; 文件属性…

STM32 Bootloader开发记录 2

在《stm32 bootloader开发记录.md》文档中&#xff0c;已经实现了Bootloader下的升级功能。可以在Bootloader启动时&#xff0c;进入升级模式&#xff0c;使用串口传输数据&#xff0c;来下载固件到flash中。 但是&#xff0c;在实际应用中&#xff0c;一般是在应用运行过程中…

基于单片机的指纹门禁设计

功能&#xff1a; 研究内容&#xff1a;本课题以单片机为核心采用C语言来开发一指纹电子密码锁。系统拟在Altium Designer9开发平台上设计原理图&#xff0c;并绘制PCB并制成单片机开发板&#xff0c;然后根据原理图将相关元器件焊接到开发板上。软件部分在Keil uVision4开发…

餐饮美食网页设计(HTML+CSS+JavaScript)

&#x1f380; 精彩专栏推荐&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业…

目标检测算法——自动驾驶开源数据集汇总2(附下载链接)

>>>深度学习Tricks&#xff0c;第一时间送达<<< 目录 一、Highway Driving 二、Mapillary Vistas 三、Cityscapes 四、CamVid >>>一起交流&#xff01;互相学习&#xff01;共同进步&#xff01;<<< 近期&#xff0c;小海带在空闲之余…

【FPGA】FPGA实现IIC协议读写EEPROM(二) -----EEPROM读写控制模块实现

EEPROM读写控制模块实现一、模块功能分析二、输入/输出信号三、EEPROM读写控制模块状态机四、EEPROM读写控制模块实现五、仿真测试写在前面&#xff1a; FPGA实现IIC协议读写EEPROM相关文章&#xff1a; IIC通信协议 【FPGA】FPGA实现IIC协议读写EEPROM&#xff08;一&#xff…

Kafka消费者之相关参数及分区分配再平衡

一、消费者重要参数 深刻的理解这些参数有利于大家在面对自己的项目场景上对配置文件有更好的把握&#xff01; 参数名称描述bootstrap.servers向 Kafka 集群建立初始连接用到的 host/port 列表。key.deserializer 和value.deserializer指定接收消息的 key 和 value 的反序列…

Spring--基于注解管理bean

基于注解管理bean 实验一&#xff1a;标记与扫描 注解 和 XML 配置文件一样&#xff0c;注解本身并不能执行&#xff0c;注解本身仅仅只是做一个标记&#xff0c;具体的功能是框架检测到注解标记 的位置&#xff0c;然后针对这个位置按照注解标记的功能来执行具体操作。 本质…

【ASM】字节码操作 工具类与常用类 asm-utils 与 asm-commons

1.概述 本章节主要是对 ASM中的 工具类与常用类 ,包asm-utils 与 asm-commons 两个包中的一些类进行讲解的介绍。 2. asm-util 在asm-util.jar当中,主要介绍CheckClassAdapter和TraceClassVisitor类。在TraceClassVisitor类当中,会涉及到Printer、ASMifier 和Textifier类。…

Vue中 引入使用 element-resize-detector 监听 Dom 元素 宽度、高度 变化

1. 前言 很多做pc端平台的小伙伴都遇到过这样一个问题&#xff1a;在做侧边栏菜单时会有一个收缩和展开的一个功能&#xff0c;在伸缩的过程中右边的页面的宽度就会随之改变。我上网查了查 &#xff0c;也动手试了试 window.onresize ()>{}。却不尽人意&#xff0c;因为它…