C++——初识模板

前言

模板是C++中的重大板块,是使C++真正超越C语言的工具,在C++模板没有设计出来之前其实C++是没有那么被行业和社会所认可的,本节我们将初步了解C++中的模板(仅作大致讲解,具体的细枝末节将会再过几节讲解),那么废话不多说,我们正式进入今天的学习

1.泛型编程

在学习模板之前,假设我们需要写一个 Swap 函数,此时我们就会用C++中的函数重载来实现,将其重载三次,如下面的代码所示:

void Swap(int& left, int& right)
{int temp = left;left = right;right = temp;
}void Swap(char& left, char& right)
{char temp = left;left = right;right = temp;
}void Swap(double& left, double& right)
{double temp = left;left = right;right = temp;
}

用函数重载来完成Swap函数的确是可以完成任务,但是这样做会有两个不足点:

1. 重载的函数仅仅是类型不同,代码复用率比较低,只要有新类型出现时,就需要用户自己增加对应的函数

2. 代码的可维护性比较低,一个出错可能所有的重载均出错

那能不能创建一个“模子”,让编译器根据不同的类型利用这个“模子”来生成代码呢?

所以C++中创建了一个模板的概念,用来把这些相似度极高且功能相同的函数整合起来。通过给这个模具中填充不同材料(类型),来获得不同材料的铸件(即生成具体类型的代码)进而实现泛型编程

泛型编程:编写与类型无关的通用代码,是代码复用的一种手段。模板是泛型编程的基础。

下面我们来看看模板的语法:

模板的关键字是:template,具体使用方法如下:

template<class T>
void Swap(T& left, T& right)
{T temp = left;left = right;right = temp;
}

函数模板是一个模具,它本身并不是一个函数,是编译器使用特定的方式产生特定的类型函数的模具,在本质上模板就是将本来应该由我们来完成的代码编写交给了编译器完成,不同的类型调用的并不是同一个函数,而是编译器在我们调用的时候自动生成的,没有减少代码量

模板分为函数模板和类模板,下面我们来一一分析:

2.函数模板

2.1函数模板的概念

函数模板代表了一个函数蓝图,函数模板与类型无关,在使用时被参数化,根据实参类型产生函数的特定类型版本

函数模板的格式:template<typename T1, typename T2,……,typename Tn>,其中关键字typename也可以使用class,二者在使用上没有任何的区别。如果有多个参数的话就要用逗号去分离。

**************************************************************************************************************

拓展讲一点,我们在日常编写代码的时候经常会遇到交换变量的情况,每次都自己写swap函数就会很麻烦。所以C++官方库中就加入了swap函数

我们一般不需要去包含这个头文件,因为在大部分情况下这个头文件都会被间接包含

**************************************************************************************************************

2.2 函数模板的实例化

刚才我们说到,如果要在模板中有多个参数就需要用逗号去分离,那么在什么情况下我们需要用到多个参数呢?我们来举一个例子:

假设我们在只有一个模板参数的条件下写Add函数:

template<class T>
T Add(const T& left, const T& right)
{return left + right;
}
int main()
{int a1 = 10, a2 = 20;double d1 = 10.0, d2 = 20.0;Add(a1, d1);Add(a2, d2);
}

该语句不能通过编译,因为在编译期间,当编译器看到该实例化时,需要推演其实参类型。通过实参a1将T推演为int,通过实参d1将T推演为double类型,但模板参数列表中只有一个T, 编译器无法确定此处到底该将T确定为int还是double类型所以报错了

要想在保持一个参数的前提下解决这个问题有两种办法:

1.推导实例化(使用强制类型转换)

int main()
{int a1 = 10, a2 = 20;double d1 = 10.0, d2 = 20.0;Add(a1, a2);Add(d1, d2);cout << Add(a1, (int)d1) << endl;cout << Add((double)a1, d1) << endl;}

使用强制类型转换在本质上解决的是推导冲突的问题,但是在某些情况下强制类型转换可能会导致结果出现错误和偏差

2.显示实例化

int main()
{int a1 = 10, a2 = 20;double d1 = 10.0, d2 = 20.0;Add(a1, a2);Add(d1, d2);cout << Add<int>(a1, d1) << endl;cout << Add<double>(a1, d1) << endl;}

显示实例化在某些情况也可能会导致结果出现错误和偏差

我们在遇到这种情况时最好是传两个模板参数

template<class T1, class T2>
T1 Add(const T1& left, const T2& right)
{return left + right;
}
int main()
{int a1 = 10, a2 = 20;double d1 = 10.0, d2 = 20.0;Add(a1, a2);Add(d1, d2);cout << Add(a1, d1) << endl;cout << Add(a1, d1) << endl;}

我们之前说过,T的类型是根据实参的类型推导出来的,我们用一个例子来解释一下:

template<class T>
T* func1(int n)
{return new T[n];
}
int main(void)
{func1(0);return 0;
}

 在这种情况下编译会失败,因为实参已经给了一个明确的类型,T此时就不能推导出类型出来,这就验证了“T是根据实参类型推导出来的”这一结论

此时要想让代码正常运行就需要运用刚才学习过的显示实例化:

int main(void)
{double* p1 = func1<double>(0);return 0;
}

2.3 函数模板的匹配机制

假设我们编写了一个与模板函数同名的具体函数,那么在具体函数和模板函数同时存在的情况下在主函数中调用函数调用的是哪一个呢?

template<class T>
T Add(const T& left, const T& right)
{cout << "T Add" << endl;return left + right;
}int Add(const int& x, const int& y)
{cout << "int Add" << endl;return x + y;
}int main(void)
{int a1 = 10, a2 = 20;Add(a1, a2);return 0;
}

我们可以看到在这种情况下,优先走了具体的函数。因为模板函数并不是现有的,还要通过模拟器自己推演生成。而在具体函数存在的情况下,编译器会优先使用现有的函数,不会去做“二次加工”

3. 类模板

3.1 类模板的相关概念

类模板的定义格式与之前的函数模板基本相同:

template<class T1, class T2 …… class Tn>
class 类模板名字
{类内成员定义
};

那么类模板在使用上有什么好处呢?我们在C语言中学习了typedef,在大部分的情况下好像也能够完成模板的功能,我们来用栈的代码来举一个例子

我们用C++的风格写出栈的代码如下:

如果我们要改成C++的模式写代码,我们需要注意:C++中没有realloc及和realloc功能类似的函数,如果要实现扩容的话就需要自己写扩容的代码(C++不设置扩容函数是有原因的,具体原因在学vector的时候我们就会知道)

template<typename T>
class Stack
{
public:Stack(size_t capacity = 4):_array(new T[n]),_size(0),__capacity(n){}~Stack(){delete[] _array;_array = nullptr;_size = _capacity = 0;}void Push(const T& x);private:T* _array;size_t _capacity;size_t _size;
};
// 模版不建议声明和定义分离到两个文件.h 和.cpp会出现链接错误,具体原因后面会讲
template<class T>
void Stack<T>::Push(const T& x)
{if (_size == _capacity){T* tmp = new T[_capacity * 2];memcpy(tmp, _array, sizeof(T) * size);delete[] _array;_array = tmp;_capacity *= 2;}_array[_size++] = x;
}
int main()
{Stack<int> st1;    // intStack<double> st2; // doublereturn 0;
}

我们需要注意:

1.定义的模板参数只能给当前的函数或者类域使用,每一个函数模板或者类模板都要定义自己的模板参数,出了函数或者类域以后当前的模板参数就无法使用。

2.模版不建议声明和定义分离到两个文件.h 和.cpp,会出现链接错误,具体原因后面会讲

我们通过代码可以知道:我们有了模板以后,可以实现一个栈存int类型的数据,一个栈存double类型的数据,而C语言中的栈就只能存一种类型的数据,要想在C语言中实现写两种类型的栈的话就需要将原代码再复制粘贴一份,再解决命名和类型的问题

3.2 类模板的实例化

类模板实例化与函数模板实例化不同,类模板都是显示实例化。类模板实例化需要在类模板名字后跟< >,然后将实例化的类型放在< >中即可,类模板名字不是真正的类,而实例化的结果才是真正的类。

// Stack是类名,Stack<int>才是类型
Stack<int> st1;    // intStack<double> st2; // double

结尾

本节我们对模板进行了初步的了解,在C++中利用模板实现泛型编程有很多优越之处。那么本节的内容就到此结束了,希望可以给您带来帮助,谢谢您的浏览!!!

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

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

相关文章

Linuxnat网络配置

&#x1f4d1;打牌 &#xff1a; da pai ge的个人主页 &#x1f324;️个人专栏 &#xff1a; da pai ge的博客专栏 ☁️宝剑锋从磨砺出&#xff0c;梅花香自苦寒来 ☁️运维工程师的职责&#xff1a;监…

一维数组--最长平台

这道题目挺简单的&#xff0c;那你还想这么久&#xff01; 直接看代码&#xff01; #include<cstdio> long long n,a[100002],sum,b[100002],max-99999,j; int main(){scanf("%d",&n);scanf("%d",&a[1]);sum1;for(int i2;i<n;i){j;scan…

【ESP32 IDF 定时器Timer】

目录 TIM定时器介绍硬件定时器和软件定时器硬件定时器基本参数硬件定时器的操作流程初始化硬件定时器设置报警注册回调函数使能和禁用定时器启动和停止定时器硬件定时器驱动代码调试 软件定时器使用软件定时器代码编写 TIM定时器 介绍 定时器是单片机内部集成&#xff0c;可以…

鸿蒙HarmonyOS开发:多种内置弹窗及自定义弹窗的详细使用指南

文章目录 一、消息提示框&#xff08;showToast&#xff09;1、导入模块2、语法3、参数4、示例5、效果 二、对话框&#xff08;showDialog&#xff09;1、导入模块2、语法3、参数4、示例5、效果 三、警告弹窗&#xff08;AlertDialog&#xff09;1、语法2、参数3、AlertDialogP…

STM32的GPIO输入输出方式设置示例

1、GPIO口做基本的输入/输出口使用时&#xff0c;输入有上拉输入、下拉输入、浮空输入&#xff08;既无上拉电阻也无下拉电阻&#xff09;3种输入方式&#xff1b;输出有开漏输出、推挽输出2种输出方式。 2、示例 &#xff08;1&#xff09;示例1&#xff1a;GPIO做输出的设置…

项目比赛经验分享:如何让即兴发言出彩

项目比赛经验分享&#xff1a;如何让即兴发言出彩 前言1. 顺势趁便法2. 词语撮要法3. 起承转合法4. 数字串连法结语 在项目管理和比赛的激烈竞争中&#xff0c;即兴发言往往成为展示个人魅力和团队精神的重要环节。如何在短时间内组织语言&#xff0c;表达清晰、有力的观点&…

基于JSP、java、Tomcat三者的项目实战--校园交易网(3)主页

前文功能的实现 技术支持&#xff1a;JAVA、JSP 服务器&#xff1a;TOMCAT 7.0.86 编程软件&#xff1a;IntelliJ IDEA 2021.1.3 x64 前文三篇登录和注册功能的实现 基于JSP、java、Tomcat、mysql三层交互的项目实战--校园交易网&#xff08;1&#xff09;-项目搭建&#xf…

Cisco路由器OSPF配置

Cisco路由器OSPF配置 本文已经迁移至 https://www.geekery.cn 本文档将详细介绍如何配置 OSPF&#xff08;开放最短路径优先&#xff09;协议&#xff0c;使用三个路由器&#xff1a;RA、RB 和 RC。一步一步说明每个路由器的配置、设置 IPv4 和 IPv6 地址的、配置OSPF 的启用以…

Cocos Creator2D游戏开发-(1)初始化设置

初心: 做一款微信或者抖音小游戏,然后发布,对于我来说这是一个新的赛道; 写这些文档的原因,记录一下自己学习过程,下次用的时候方便找 cocos creator版本: 3.8.3 当前小游戏飞机大战教程来源于: 抖音: 禅影 chanying001 源码目录: https://www.kdocs.cn/l/caLr6XCbEfPa 创建一个…

zeal 开发者离线文档工具

zeal是一款程序开发者不可或缺的离线文档查看器 下载地址 官网地址&#xff1a; windows版csdn下载(开箱即用含)&#xff1a;https://download.csdn.net/download/xzzteach/89588765 已离线 Android.docset Apache_HTTP_Server.docset Bash.docset Bootstrap_4.docset Bootst…

selenium入门超详细教程——网页自动化操作

文章目录 简介一、环境安装1.selenium安装2.安装浏览器驱动2.1 确定浏览器版本2.2 下载驱动 二、基础用法1.对页面进行操作1.1 初始化浏览器对象1.2 访问页面1.3 设置浏览器大小1.4 前进后退1.5 获取页面基础属性 2.定位页面元素3.模拟鼠标操作3.1 常用操作 4.模拟键盘操作4.1 …

测评Copilot和ChatGPT-4o从PDF创建PPT功能

关于 Copilot 由 PDF 创建 PPT&#xff0c;之前已经发过几篇文档说明&#xff1a; Copilot for Microsoft 365 现已支持从PDF创建Word和PPT Copilot读PDF文献一键生成PPT&#xff0c;吓傻了导师 Copilot一键将PDF转为PPT&#xff0c;治好了我的精神内耗 今天来测评对比 Cop…

在LabVIEW中使用Modbus

NI 提供了三种主要机制与 Modbus 设备进行接口&#xff1a;(1) 高级 OPC 服务器&#xff0c;(2) Modbus I/O 服务器&#xff0c;以及 (3) 在 NI LabVIEW 软件中通过 LabVIEW 实时或 LabVIEW 数据记录与监控控制&#xff08;DSC&#xff09;模块引入的低级 Modbus API。 LabVIE…

黑马Java零基础视频教程精华部分_8_学生管理系统

系列文章目录 文章目录 系列文章目录一、业务分析二、结合业务流程图编写代码1、Student.java代码&#xff1a;2、StudentSystem.java代码&#xff1a;3、标号&#xff08;‌label&#xff09;‌ 三、学生管理系统升级版 一、业务分析 需求文档如图所示&#xff1a; 根据需求…

【初阶数据结构篇】单链表的实现(赋源码)

文章目录 单链表的实现代码位置概念与结构概念&#xff1a;结构&#xff1a; 链表的性质链表的分类单链表的实现单链表的创建和打印及销毁单链表的创建单链表的打印单链表的销毁 单链表的插入单链表头插单链表尾插单链表在指定位置之前插入数据单链表在指定位置之后插入数据 单…

SAP MM学习笔记47 - 实地棚卸(库存盘点)

上一章讲了SAP MM模块种的出力管理&#xff08;消息管理&#xff09;。 SAP MM学习笔记46 - 购买中的出力管理(消息管理)-CSDN博客 本章讲库存盘点的相关内容。 - 库存盘点分3步走&#xff1a;实地棚卸票登录&#xff0c;检数入力&#xff0c;差异分析及转记 - SAP中还提供了…

学习笔记 韩顺平 零基础30天学会Java(2024.7.22)

P407 接口使用细节2 P407 接口课堂练习 对于最后一个的输出&#xff1a;B因为实现了A的接口&#xff0c;所以和继承一样&#xff0c;B可以访问A的变量 P409 接口VS继承 接口对单继承机制&#xff08;是指只能继承一个类&#xff09;进行了补充 也可以理解为&#xff0c;子类通过…

24种设计模式介绍与6大设计原则(电子版教程)

前言 您是一个初级的 coder,可以从中领会到怎么设计一段优秀的代码&#xff1b;您是一个高级程序员&#xff0c;可以从中全面了解到设计模式以及 Java 的边角技术的使用&#xff1b;您是一个顶级的系统分析师&#xff0c;可以从中获得共鸣&#xff0c;寻找到项目公共问题的解决…

安全哈希算法:SHA算法

&#x1f3af; 主题简介 SHA&#xff08;Secure Hash Algorithm&#xff09;是比MD5更安全的哈希算法。通过案例形式了解SHA算法的原理、实现方法及注意细节。无论你是Python爱好者还是JavaScript高手&#xff0c;这篇内容都将为你提供一个深入了解SHA算法的机会。 &#x1f…