模板-初阶

引言:

在C++,我们已经学过了函数重载,这使得同名函数具有多个功能。但是还有一种更省力的方法:采用模板。

本文主要介绍以下内容

1. 泛型编程
2. 函数模板
3. 类模板

1.泛型编程

在将这一部分之前,通过一个故事引入这个知识点。假设这天你干活回家,瘫坐在沙发上,玩起来心爱的古董。当你刚碰到古董的时候,你突然穿越到了古代:三国。诸葛亮刚写了出师表,需要你一晚上誊写出1000份,第二天分发给大家观看,那你该怎么一晚上完成这么繁杂的任务呢?这时候你穿越到了现实世界,带了一个模子回去,模子上刻着字,这样只需要蘸蘸墨水把纸张放上去,便可以高效完成印刷。

可以说泛型编程就是基于这样的思想,我们只需要有一个模板,就可以完成大量的重复操作。而重复操作正是机器擅长的任务,这重复操作就交给了我们的“苦力”--编译器。

这是Swap系列的函数重载,我们如何实现一个通用的交换函数呢?


void Swap(int& left, int& right)
{int temp = left;left = right;right = temp;
}
void Swap(double& left, double& right)
{double temp = left;left = right;right = temp;
}
void Swap(char& left, char& right)
{char temp = left;left = right;right = temp;
}
使用函数重载虽然可以实现,但是有一下几个不好的地方:
1. 重载的函数仅仅是类型不同,代码复用率比较低,只要有新类型出现时,就需要用户自己增加对应的函
2. 代码的可维护性比较低,一个出错可能所有的重载均出错
那能否告诉编译器一个模子,让编译器根据不同的类型利用该模子来生成代码呢?
这时C++的一个关键字便登场了---template(模板)
如果在C++中,也能够存在这样一个模具,通过给这个模具中填充不同材料(类型),来获得不同材料的铸件 (即生成具体类型的代码),那将会节省许多头发。巧的是前人早已将树栽好,我们只需在此乘凉。
泛型编程:编写与类型无关的通用代码,是代码复用的一种手段。模板是泛型编程的基础。
模板分为类模板与函数模板。

2. 函数模板

函数模板概念
函数模板代表了一个函数家族,该函数模板与类型无关,在使用时被参数化,根据实参类型产生函数的特定类型版本。
针对上面的Swap函数,便可以利用函数模板进行操作。

函数模板格式:

template<typename T1, typename T2,......,typename Tn>           //用的是尖括号     
返回值类型 函数名(参数列表){}

template<class T>
void Swap(T& t1, T& t2)
{T tmp = t1;t1 = t2;t2 = tmp;
}
注意: typename 用来定义模板参数 关键字 也可以使用 class( 切记:不能使用 struct 代替 class)

int main()
{int a = 10;int b = 20;double c = 1.1;double d = 2.2;cout << "before : a = " << a << " b = " << b << endl;cout << "before : c = " << c << " d = " << d << endl;Swap(a, b);Swap(c, d);cout << "after : a = " << a << " b = " << b << endl;cout << "after : c = " << c << " d = " << d << endl;return 0;
}

这是模板函数的使用,可以正常使用!

需要注意的是:这两个Swap调用的是不同的函数!

函数模板的原理
那么如何解决上面的问题呢?大家都知道,瓦特改良蒸汽机,人类开始了工业革命,解放了生产力。机器生产淘汰掉了很多手工产品。本质是什么,重复的工作交给了机器去完成。有人给出了论调:懒人创造世界。
函数模板是一个蓝图,它本身并不是函数,是编译器用使用方式产生特定具体类型函数的模具。所以其实模板就是将本来应该我们做的重复的事情交给了编译器。
在编译器编译阶段,对于模板函数的使用,编译器需要根据传入的实参类型来推演生成对应类型的函数以供调用。比如:当用double类型使用函数模板时,编译器通过对实参类型的推演,将T确定为double类型,然后产生一份专门处理double类型的代码,对于字符类型也是如此。
函数模板的实例化
用不同类型的参数使用函数模板时,称为函数模板的实例化。模板参数实例化分为:隐式实例化和显式实例化
隐式实例化:让编译器根据实参推演模板参数的实际类型。
我们使用的Swap函数传参方式就是一种隐式实例化的方式。
让我们分析这两段代码。
代码一:

a c是不同的类型,因此无法对模板的使用。如果我们对c进行强转成int之后,在Swap函数内部进行引用的时候,发生了权限的放大。
这主要是强转的过程发生的。 在强转时,会生成一个临时对象,临时对象具有常性,必须用const修饰的引用才能接收!而Swap函数的参数 接收这个临时对象时,没有用const修饰,所以才会报错,这是出现了权限的放大。
代码二:
这串代码编译成功,主要是因为Add内部的参数被const修饰,因此可以完成引用传参。
当然,代码二也可以采用显式实例化去完成编译。
显式实例化:在函数名后的<>中指定模板参数的实际类型
以下是显式实例化的例子:
告诉模板,统一成int
如果类型不匹配,编译器会尝试进行隐式类型转换,如果无法转换成功编译器将会报错。
对于形参没有T时,一般采用显式实例化。
模板参数的匹配原则
1.一个非模板函数可以和一个同名的函数模板同时存在,而且该函数模板还可以被实例化为这个非模板函数
// 专门处理int的加法函数
int Add(int left, int right)
{return left + right;
}
// 通用加法函数
template<class T>
T Add(T left, T right)
{return left + right;
}
void Test()
{Add(1, 2); // 与非模板函数匹配,编译器不需要特化Add<int>(1, 2); // 调用编译器特化的Add版本
}

2.对于非模板函数和同名函数模板,如果其他条件都相同,在调动时会优先调用非模板函数而不会从该模板产生出一个实例。如果模板可以产生一个具有更好匹配的函数, 那么将选择模板。

// 专门处理int的加法函数
int Add(int left, int right)
{return left + right;
}// 通用加法函数
template<class T1, class T2>
T1 Add(T1 left, T2 right)    //注意返回类型是T1
{return left + right;
}void Test()
{Add(1, 2); // 与非函数模板类型完全匹配,不需要函数模板实例化Add(1, 2.0); // 模板函数可以生成更加匹配的版本,编译器根据实参生成更加匹配的Add函
数
}
3.模板函数不允许自动类型转换,但普通函数可以进行自动类型转换

3.类模板

当我们实现Stack类的时候,我们用typedef int DataType;处理。但如果我们想同时利用Stack类实例化的对象st1 st2去存储int和double类型的数据,此时就会出现冲突。依次便出现了类模板。
类模板的定义格式
template < class T1 , class T2 , ..., class Tn >
class 类模板名
{
// 类内成员定义
};
这是stack的模板类

template<class T>	//存储的数据类型是T
class Stack
{
public:Stack(int capcaity = 4):_capacity(capcaity), _top(0),_array(nullptr){_array = new T[_capacity];		//开辟T类型的数组}void Push(const T& x)    //插入T类型的数据。普通函数别忘了写返回类型!{_array[_top] = x;_top++;//容量_capacity不需要++}~Stack(){delete[] _array;_array = nullptr;}
private:T* _array;    //类型是Tint _top;int _capacity;
};
类模板的实例化
类模板实例化与函数模板实例化不同,类模板实例化需要在类模板名字后跟<>,然后将实例化的类型放在<> 中即可,类模板名字不是真正的类,而实例化的结果才是真正的类。
这是Stack类的实例化。实例化时必须显式实例化,来告知编译器存储的类型是什么。
需要注意的是 Stack是类名,而Stack<T>才是实例化的类型。我们创建对象,需要用类型 + 对象名才能实例化
类模板的函数不要声明和定义分离到两个文件(.cpp,.h)否则会报错。

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

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

相关文章

mysql中EXPLAIN详解

大家好。众所周知&#xff0c;MySQL 查询优化器的各种基于成本和规则的优化会后生成一个所谓的执行计划&#xff0c;这个执行计划展示了接下来具体执行查询的方式。在日常工作过程中&#xff0c;我们可以使用EXPLAIN语句来查看某个查询语句的具体执行计划&#xff0c; 今天我们…

数据库索引的理解

目录 1.索引是什么&#xff0c;解决了什么问题 2.索引付出了什么代价 3.如何使用sql索引&#xff0c;有何注意事项 普通索引&#xff1a; 唯一索引&#xff1a; 主键索引(Primary Key Index)&#xff1a; 删除索引: 创建主键索引的基本语法: 4.索引背后的数据结构 1.索…

数据结构严蔚敏版精简版-绪论

1.基本概念和术语 下列概念和术语将在以后各章节中多次出现&#xff0c;本节先对这些概念和术语赋予确定的含义。 数据(Data)&#xff1a;数据是客观事物的符号表示&#xff0c;是所有能输入到计算机中并被计算机程序处理的符号 的总称。 数据元素(DataElement)&#xff1a;…

《Python学习》-- 实操篇一

一、文件操作 1. 1 读取文本文件 # 文件操作模式 # r (默认) - 只读模式。文件必须存在&#xff0c;否则会抛出FileNotFoundError。在这种模式下&#xff0c;你只能读取文件内容&#xff0c;不能写入或追加。 # w - 写入模式。如果文件存在&#xff0c;内容会被清空&#xff…

大模型日报2024-06-02

大模型日报 2024-06-02 大模型资讯 LLM360推出K2&#xff1a;开源大语言模型&#xff0c;计算效率超越Llama 2 70B 摘要: LLM360发布了K2&#xff0c;一款完全可复现的开源大语言模型。K2在计算效率上超越了Llama 2 70B&#xff0c;使用的计算能力减少了35%。这一突破性的模型为…

JAVA: 抽象类和接口

Java中可以可以定义不含方法体的方法&#xff0c;方法的方法体由其所在类的子类根据实际需求去实现&#xff0c;这样的方法称为抽象方法&#xff08;Abstract Method&#xff09;&#xff0c;包含抽象方法的类必须是抽象类&#xff08;Abstract Class&#xff09;。 抽象方法和…

【架构设计】Java如何利用AOP实现幂等操作,防止客户端重复操作

1实现方案详解 在Java中,使用AOP(面向切面编程)来实现幂等操作是一个常见的做法,特别是当你想在不修改业务代码的情况下添加一些横切关注点(如日志、事务管理、安全性等)时。幂等操作指的是无论执行多少次,结果都是相同的操作。 为了利用AOP实现幂等操作以防止客户端重…

基于STM32的水库预警系统的Proteus仿真

文章目录 一、水库预警系统1.题目要求2.思路2.1 OLED显示汉字2.2 水质传感器等等2.3 步进电机2.4 驱动水泵 3.仿真图3.1 未仿真时3.2 开始仿真&#xff0c;OLED开始显示3.3 提高水位&#xff0c;开启阀门和预警3.4 通过按键增大水位阈值&#xff0c;取消报警 4.仿真程序4.1 程序…

轻松拿捏C语言——【文件操作】

&#x1f970;欢迎关注 轻松拿捏C语言系列&#xff0c;来和 小哇 一起进步&#xff01;✊ &#x1f389;创作不易&#xff0c;请多多支持&#x1f389; &#x1f308;感谢大家的阅读、点赞、收藏和关注&#x1f495; &#x1f339;如有问题&#xff0c;欢迎指正 目录 &#x1f…

关于模拟信道和数字信道根本区别的探讨

在学习过程中&#xff0c;了解到模拟信号、数字信号是什么&#xff0c;以及模拟信道只能传输模拟信号&#xff0c;数字信道只能传输数字信号&#xff0c;模拟信道要传输数字信号&#xff0c;需要经过调制解调器将数字信号调制成模拟信号再传输。也了解到&#xff0c;电话线是属…

谨以此文章记录我的蓝桥杯备赛过程

以国优秀结束了蓝桥杯cb组 鄙人来自电信学院&#xff0c;非科班出身&#xff0c;在寒假&#xff0c;大约2024年2月份&#xff0c;跟着黑马程序员将c基础语法学完了&#xff0c;因为过年&#xff0c;事情较多&#xff0c;没在学了。 最初就是抱着拿省三的态度去打这个比赛的&a…

C语言之旅:探索单链表

目录 一、前言 二、实现链表的功能&#xff1a; 打印 创建节点 尾插 尾删 头插 头删 查找 在指定位置之前插入数据 指定位置删除 在指定位置之后插入数据 打印 销毁 三、全部源码&#xff1a; 四、结语 一、前言 链表是一个强大且基础的数据结构。对于很多初…

禁用手机连接 - Win11

问题 Win11系统自带手机连接软件&#xff0c;会在后台自启&#xff0c;不适用于全部的手机型号&#xff0c;而且常规方法无法卸载。甚至任务管理器中&#xff0c;此软件的后台进程高达76个&#xff0c;如下图。下文以Win11系统为例&#xff0c;介绍如何禁用手机连接。 解决方…

考研数学:有些无穷小不能用等价无穷小的公式?

今天要给大家分享的笔记是&#xff1a;《有些无穷小虽然是无穷小&#xff0c;但却不能用无穷小的相关公式》&#xff1a;

Winform GDI+双缓冲绘图与GDI API双缓冲绘图

一、GDI双缓冲绘图 BufferedGraphicsContext GraphicsContext BufferedGraphicsManager.Current;BufferedGraphics myBuffer GraphicsContext.Allocate(e.Graphics, e.ClipRectangle);Graphics g myBuffer.Graphics;Bitmap bitmap bitmaps[index];g.DrawImage(bitmap, 0, 0…

来自大厂硬盘的降维打击!当希捷酷玩520 1TB SSD卷到369,请问阁下该怎么应对?

来自大厂硬盘的降维打击&#xff01;当希捷酷玩520 1TB SSD卷到369&#xff0c;请问阁下该怎么应对&#xff1f; 哈喽小伙伴们好&#xff0c;我是Stark-C~ 今年4月份的时候因为电脑上的游戏盘突然挂掉&#xff0c;为了性价比选购了希捷酷玩520 1TB SSD&#xff0c;同时我也是…

vue3加axios配合element-plus实现图片等文件本地上传,并获取服务器返回的真实地址数据,前端写法

小白写法嘿嘿 开发工具和关键词 开发工具&#xff1a; vscode 关键词&#xff1a;vue3、element-plus、axios 后端 后端业务逻辑处理使用的是unicloud的云函数&#xff0c;大家可以看我上一篇文章。 思路 1、禁止element-plus的el-upload组件自动上传&#xff0c;变成手动上传…

装修10个容易被遗忘的开关插座位置

雅静说家里开关插座哪些最容易忘记了留?      一共10个,你看看有少的吗&#xff0c;我家水电师父就没有留够      来了又重新补的很麻烦,记得收藏      1,大门口上边留一个,后期可以安装监控      特别家里有老人和小孩,以及经常来快递的      2,弱电箱留…

7. MySQL 视图、索引

文章目录 【 1. 视图 View 】1.1 视图原理1.2 创建视图 CREATE VIEW1.2.1 创建基于单表的视图1.2.2 创建基于多表的视图 1.3 查看视图1.3.1 查看视图的内容1.3.2 查看视图的详细信息 1.4 修改视图 ALTER VIEW1.4.1 修改视图内容1.4.2 修改视图名称 1.5 删除视图 DORP VIEW 【 2…

961题库 北航计算机 计算机网络 附答案 选择题形式

有题目和答案&#xff0c;没有解析&#xff0c;不懂的题问大模型即可&#xff0c;无偿分享。 第1组 习题 OSI 参考模型的第 5 层( 自下而上 ) 完成的主要功能是 A. 差错控制 B. 路由选择 C. 会话管理 D. 数据表示转换 100BaseT 快速以太网使用的导向传输介质是 A. 双绞线 B. …