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.rhkb.cn/news/382778.html

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

相关文章

Qt多语言功能实现

本文介绍Qt多语言功能实现。 应用程序多语言支持是常用功能&#xff0c;比如产品需要出口到不同语种的国家。采用Qt的多语言支持工具可以方便实现应用程序的多语言功能。本文以中英文语言切换为例&#xff0c;简要介绍Qt的多语言功能实现。 1.界面设计 界面设计需要考虑使用…

【数据分享】2013-2022年我国省市县三级的逐日SO2数据(excel\shp格式\免费获取)

空气质量数据是在我们日常研究中经常使用的数据&#xff01;之前我们给大家分享了2000——2022年的省市县三级的逐日PM2.5数据和2013-2022年的省市县三级的逐日CO数据&#xff08;均可查看之前的文章获悉详情&#xff09;&#xff01; 本次我们分享的是我国2013——2022年的省…

数据隐私保护与区块链技术的结合:新兴趋势分析

在当今数字化时代&#xff0c;数据隐私保护成为了一个备受关注的重要话题。随着个人数据的不断生成和流通&#xff0c;如何有效保护用户的隐私成为了技术创新的一个重要方向。区块链技术作为一种去中心化、安全性高且可追溯的技术手段&#xff0c;正在逐渐成为解决数据隐私保护…

golang 基础 泛型编程

&#xff08;一&#xff09; 示例1 package _caseimport "fmt"// 定义用户类型的结构体 type user struct {ID int64Name stringAge uint8 }// 定义地址类型的结构体 type address struct {ID intProvince stringCity string }// 集合转列表函数&#…

杰发科技Bootloader(2)—— 基于7840的Keil配置地址

序 在7840的sample代码里面有一个简单的Boot跳转APP的示例 PFlash地址从0开始 DFlash的地址从1000000开始 Boot解析 他的boot地址配置为0 Boot的代码主要是这几行&#xff0c;主要作用就是Flash的跳转 int main(void) {SystemClock_Config();InitDebug();printf("demo…

Leetcode 721.账户合并(hash+dfs)☆

思路&#xff1a; 最核心的地方在于如何合并&#xff1f;这里是通过具有相同的email进行账户的合并&#xff0c;这个相同的email类似于图中的共同节点将两个账户连接起来&#xff0c;所以将原来 账户名 -> 邮件1 邮件2.。。变成hash 邮件1 ->账户id1&#xff0c;账户id2…

ModelArts中sinh算子的开发

一、环境配置 1、创建notebook并连接 使用ModelArts新建一个notebook,我这里镜像选择第一个,里面含有cann和Ascend910处理器,我这里环境只能使用ssh连接,创建一个密钥对,保存到C盘中的user/Administrator/目录下。 在网页中选择使用vscode接入,等待vscode打开后,选择密…

【数据结构初阶】一篇文章带你超深度理解【单链表】

hi &#xff01; 目录 前言&#xff1a; 1、链表的概念和结构 2、单链表&#xff08;Single List&#xff0c;简写SList&#xff09;的实现 2.1 定义链表&#xff08;结点&#xff09;的结构 2.2 创建一个链表 2.3 打印链表 2.4 尾插 2.5 头插 2.6 尾删 2.7 头…

PT2262-IR

PT2262是一款很古老的编码芯片&#xff0c;其兼容型号有&#xff1a;SC2262&#xff0c;AD2262&#xff0c;SC2260(需改变匹配电阻)等。 依据其datasheet&#xff0c;PT2262射频模式工作原理: CODE BITS A Code Bit is the basic component of the encoded waveform, and ca…

Docker核心技术:Docker原理之Cgroups

云原生学习路线导航页&#xff08;持续更新中&#xff09; 本文是 Docker核心技术 系列文章&#xff1a;Docker原理之Cgroups&#xff0c;其他文章快捷链接如下&#xff1a; 应用架构演进容器技术要解决哪些问题Docker的基本使用Docker是如何实现的 Docker核心技术&#xff1a;…

Maven学习——Maven的下载、安装与配置(详细攻略!)

目录 前言 1.下载与安装 2.配置Maven的环境变量 3.配置Maven的本地仓库 4. 配置Maven的镜像远程仓库 前言 我在之前写了一篇博客&#xff0c;是介绍Maven的基本概念和下载安装&#xff0c;但是由于篇幅过长&#xff0c;Maven的下载与安装写的并不详细&#x1f436;&#x…

Windows系统设置暂停更新,暂停时间可达3000天,“永久”暂停更新,亲测有效

好多小伙伴被Windows系统的更新搞得很烦&#xff0c;经常在使用中自己下载更新包&#xff0c;占用网路资源&#xff0c;过段时间就要更新&#xff0c;特别讨厌 今天教你一招&#xff0c;可以暂停更新长达3000天&#xff0c;亲测有效 1、打开系统CMD命令执行窗口&#xff0c;输…

Ideal窗口中左右侧栏消失了

不知道大家在工作过程中有没有遇到过此类问题&#xff0c;不论是Maven项目还是Gradle项目&#xff0c;突然发现Ideal窗口右侧图标丢失了&#xff0c;同事今天突然说大象图标不见了&#xff0c;不知道怎样刷新gradle。 不要慌张&#xff0c;下面提供一些解决思路&#xff1a; 1…

超声波俱乐部:AI应用大爆发前夜,场景、闭环与LLM进化

7月13日&#xff0c;第十九期超声波俱乐部内部分享会在北京望京举行&#xff0c;本期的主题是&#xff1a;AI应用大爆发前夜&#xff0c;场景、闭环与LLM进化。 到场的嘉宾有&#xff1a;超声波创始人杨子超&#xff0c;超声波联合创始人、和牛商业创始人刘思雨&#xff0c;豆…

硅纪元视角 | 语音克隆突破:微软VALL-E 2,Deepfake新纪元!

在数字化浪潮的推动下&#xff0c;人工智能&#xff08;AI&#xff09;正成为塑造未来的关键力量。硅纪元视角栏目紧跟AI科技的最新发展&#xff0c;捕捉行业动态&#xff1b;提供深入的新闻解读&#xff0c;助您洞悉技术背后的逻辑&#xff1b;汇聚行业专家的见解&#xff0c;…

IP协议和路由转发

文章目录 IP协议IP报头网段划分特殊的IP私有IP和公有IP IP分片 路由 IP协议 IP协议提供了一种能力&#xff0c;将数据报从A主机送到B主机&#xff0c;TCP可以保证可靠性&#xff0c;所以TCP/IP协议可以将数据可靠的从A主机送到B主机。 IP报头 4位版本号(version): 指定IP协议…

Java 面试 | Redis

目录 1. 在项目中缓存是如何使用的&#xff1f;2. 为啥在项目中要用缓存&#xff1f;3. 缓存如果使用不当会造成什么后果&#xff1f;4. redis 和 memcached 有什么区别&#xff1f;5. redis 的线程模型是什么&#xff1f;6. 为什么单线程的 redis 比多线程的 memcached 效率要…

android13禁用某个usb设备

总纲 android13 rom 开发总纲说明 目录 1.前言 2.触摸设备查看 3.功能修改 3.1 禁用usb触摸 3.2 禁用usb键盘 3.3 禁用usb遥感 4.查看生效与否 5.彩蛋 1.前言 用户想要禁止使用某些usb设备,需要系统不能使用相关的usb设备,例如usb触摸屏,usb键盘,usb遥感等等usb…

Unity:PC包直接查看Log日志

PC端会输出Log日志&#xff0c;位置在&#xff1a; C:\Users\用户名\AppData\LocalLow\公司名\项目名 在这里可以找到类似的文件&#xff1a; 打开便可以看到打印。

C++ 设计模式(五)——状态模式

状态模式 序言理解源码 序言 设计模式只是一个抽象的设计模式方法&#xff0c;并不是一个固定使用的搭配&#xff0c;就算是普通switch语句&#xff0c;Map&#xff0c;乃至状态机都是状态模式的其中一种实现方法 状态模式看起来好像和策略模式差不多&#xff0c;主要是其的侧…