C++之模版初阶(简单使用模版)

前言

        在学习C++的模版之前,咱们先来说一说模版的概念,模版在我们的日常生活中非常常见,比如我们要做一个ppt,我们会去在WPS找个ppt的模版,我们只需要写入内容即可;比如我们的数学公式,给公式套值,就可以算出结果;比如我们在写实验报告,老师会给一个实验报告的模版,我们按照里面的模版直接写入内容即可;所以生活中的模版就是相当于提供了一个事物的框架,我们只需要输入主要的内容就行了。那C++里面的模版也是如此,了解到了这里,就一起开始学习C++的模版吧!

一、函数模版

        我们在C++上学过一个函数重载,函数重载就是通过形参类型的不同,他们被分为不同的函数,虽然函数名相同;比如我们要实现一个交换函数,如下代码:

void Swap(int& a, int& b)
{int tmp = a;a = b;b = tmp;
}
void Swap(double& a, double& b)
{double tmp = a;a = b;b = tmp;
}
void Swap(char& a, char& b)
{char tmp = a;a = b;b = tmp;
}

        我们会发现,这作为一个交换函数未免也太麻烦了吧,虽然可以实现不同类型的参数的交换,但是这样的代码多少回=会显得冗余(多余),基于这样的原因,C++创造了函数模版。

1. 函数模版的定义

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

        如何理解这句话呢?其实就是我给你一个函数的模版,比如交换函数的模版,你只需要填入实参的不同类型,编译器自动识别,给你匹配相应的函数。

        其实这属于一种泛型编程,泛型编程指的就是编写与类型无关的通用代码,具有通用性,而模版是泛型编程的基础

2. 函数模版的格式

        template 指的是模版,要写函数模版之前,必须要指定模版的参数类型,也就是说T代表的就是模版,T就是可以换成任何数据类型的;而class / typename 是定义模版参数的关键字,可以在<>定义多个模版参数,用逗号分开即可。

        下面的代码,就是一个函数模版,我们要实现一个交换函数,那要设计的模版是谁呢?因为不同的类型都要实现交换,所以我们的模版参数要放到形参的位置上,让我们在传不同类型的实参时,可以顺利完成对应的交换。

其中template <class T>中的T是虚拟类型

#include <iostream>
using namespace std;
template<class T>
void Swap( T& left, T& right)
{T temp = left;left = right;right = temp;
}
int main()
{int a = 3;int b = 4;Swap(a, b);double c = 2.5;double d = 6.6;Swap(c, d);char ch1 = 'x';char ch2 = 'y';Swap(ch1, ch2);cout << a << ' ' << b << endl;cout << c << ' ' << d << endl;cout << ch1 << ' ' << ch2 << endl;return 0;
}

        在调用函数的时候直接传我们想交换的元素就可以,剩下的都是编译器做的事情。所以我们本来针对不同的数据类型要实现不同的函数重载,但有了函数模版之后,我们就只需要写一个函数了,剩下的是编译器帮我们完成的。不得不感慨一下,真的是懒人创造世界!

3. 函数模版的图示

        在编译器编译阶段,对于模板函数的使用,编译器需要根据传入的实参类型来推演生成对应类型的函数以供 调用。比如:当用double类型使用函数模板时,编译器通过对实参类型的推演,将T确定为double类型,然 后产生一份专门处理double类型的代码,对于字符类型也是如此。

4. 模版参数的实例化

函数模版的实例化,也就是相当于去调用这个函数模版,我们上面看到的是模版参数是在形参这里的,那如果我们没有在形参中使用模版参数呢?这样如何告诉模版参数是什么数据类型呢?所以在函数模版的实例化分为显式实例化和隐式实例化

4.1 隐式实例化

        隐式实例化就是并没有在函数调用的时候,明确指出模版参数的数据类型,比如下面的代码:

#include <iostream>
using namespace std;
template<class T>
void Swap( T& left, T& right)
{T temp = left;left = right;right = temp;
}
int main()
{int a = 3;int b = 4;Swap(a, b);return 0;
}

4.2 显式实例化

        显式实例化,就是在函数调用时,显式的写出了模版参数的数据类型,如下图代码:

#include <iostream>
using namespace std;
template<class T>
void Swap( T& left, T& right)
{T temp = left;left = right;right = temp;
}
int main()
{int a = 3;int b = 4;Swap<int>(a, b);return 0;
}

大家可能会觉得,这个显式实例化好像挺多余的,因为我们在函数传参的时候就已经隐式的传递了模版参数的数据类型了,那大家再来看下面的代码:

#include <iostream>
using namespace std;
template<class T>
T GetNum(int n)
{T b = n;return b;
}
int main()
{int a = 3;int b = 4;int c = GetNum(a);return 0;
}

如果我们没办法从传参这里让编译器得知模版参数的数据类型呢?我们这样写是不对的,所以我们不能隐式的传了。必须显式的写,正确的代码如下:

#include <iostream>
using namespace std;
template<class T>
T GetNum(int n)
{T b = n;return b;
}
int main()
{int a = 3;int b = 4;int c = GetNum<int>(a);return 0;
}

所以这种情况我们必须显式实例化,<>里面就是告诉模版参数的数据类型。

5. 如何应对不同数据类型的运算

如果我们要实现一个int和double进行加法,我们下面代码还可以实现吗?

#include <iostream>
using namespace std;
template<class T>
T Add(const T& a, const T& b)
{return a + b;
}
int main()
{int a = 3;double b = 4.7;cout << Add(a, b) << endl;return 0;
}

        我们要记住,对于模版函数,不允许类型转换的,所以a是int类型,先传入给T,T就是int类型,又因为不会类型转换,所以会报错的。那应该如何实现呢?我们直接给出最优方案:

#include <iostream>
using namespace std;
template<class T, class Y>
Y Add(const T& a, const Y& b)
{return a + b;
}
int main()
{int a = 3;double b = 4.7;cout << Add(a, b) << endl;return 0;
}

        我们只需要加一个模版参数就可以了,但是要知道返回值的类型必须是类型提升最后的类型,这是什么意思呢?

        因为不同类型的运算,会发生类型的提升

        char ——> int ——> float ——> double  

        所以要记住这里就OK了

6. 模版参数的匹配规则

1、 合适匹配的情况下,有现成的就匹配现成的
2、没有合适的,就将就用(指没有函数模版的时候)
3、有更合适就用更合适的,哪怕要自己使用函数模版创造

二、类模版

1. 类模版的格式

其实也跟函数模版差不多,就是我们要知道的是,模版参数给谁就行,其他的都不动。

template <class T>
class Stack
{
public:Stack(int capacity = 4): _a(new T[capacity]), _size(0), _capacity(capacity){}~Stack(){delete []_a;_a = nullptr;_size = _capacity = 0;}
private:T *_a;int _size;int _capacity;
};

2. 类模版的类名、类类型和类模版的实例化

        这里想说的是,我们定义了类模版之后,类名是什么?类类型是什么?类模版又是如何实例化的呢?

Stack<int> st;

上面的代码是正确调用这个类,那类名还是Stack,而这个类模版的类类型就不是类名类,

类名+显式实例化模版参数

#include <iostream>
using namespace std;
template <class T>
class Stack
{
public:Stack(int capacity = 4): _a(new T[capacity]), _size(0), _capacity(capacity){}~Stack(){delete []_a;_a = nullptr;_size = _capacity = 0;}
private:T *_a;int _size;int _capacity;
};
int main()
{Stack<int> st;return 0;
}

3. 在类模版外的函数定义

这里需要重点注意一下,我们函数定义可以在类内部,也可以在类外部,但是在类外部需要注意一下简单的规则。

1. 类模版外的函数定义,必须在同一个文件里

2. 需要指定域空间和模版

#include <iostream>
using namespace std;template <class T>
class Stack
{
public:Stack(int capacity = 4): _a(new T[capacity]), _size(0), _capacity(capacity){}~Stack();
private:T *_a;int _size;int _capacity;
};template<class T>
Stack<T>::~Stack()
{delete []_a;_a = nullptr;_size = _capacity = 0;
}int main()
{Stack<int> st;return 0;
}

三、使用模版的注意事项

1. 模板运行时不检查数据类型,也不保证类型安全,相当于类型的宏替换;

2. 模板与类型无关,提高了代码复用性;

3. 只要支持模板语法,模板的代码就是可移植的;也就是可移植性好,跨平台性;

4. 类模板是一个类家族,模板类是通过类模板实例化的具体类

5. 类模板的成员函数都是模板函数

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

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

相关文章

【Linux】基本指令(二)

本文续接上文基本指令&#xff08;一&#xff09; 目录 cpmvcatmore && less cp 语法&#xff1a;cp [选项] 源文件或目录 目标文件或目录 功能: 复制文件或目录 说明: cp指令用于复制文件或目录&#xff0c;如同时指定两个以上的文件或目录&#xff0c;且最后的目的地…

第一节HarmonyOS DevEcoStudio工具下载以及环境搭建

一、下载与安装DevEco Studio 在HarmonyOS应用开发学习之前&#xff0c;需要进行一些准备工作&#xff0c;首先需要完成开发工具DevEco Studio的下载与安装以及环境配置。 进入DevEco Studio 工具下载官网&#xff1a;https://developer.harmonyos.com/cn/develop/deveco-stu…

经典滑动窗口试题(二)

&#x1f4d8;北尘_&#xff1a;个人主页 &#x1f30e;个人专栏:《Linux操作系统》《经典算法试题 》《C》 《数据结构与算法》 ☀️走在路上&#xff0c;不忘来时的初心 文章目录 一、水果成篮1、题目讲解2、讲解算法思路3、代码实现 二、找到字符串中所有字母异位词1、题目…

距离向量路由协议——IGRP和EIGRP

IGRP-内部网关路由协议 IGRP&#xff08;Interior Gateway Routing Protocol&#xff0c;内部网关路由协议&#xff09;是一种动态距离向量路由协议&#xff0c;它是Cisco公司在20世纪80年代中期设计的&#xff0c;是Cisco专用路由协议。目前在Cisco高版本的IOS已经对IGRP不提…

交叉编译

1. 交叉开发 交叉编译&#xff1a; 在电脑把程序编写 编译 调试好 再下载到嵌入式产品中运行 编译&#xff1a; gcc 之前编译环境和运行环境是一样的 交叉编译&#xff1a; 编译 把编译代码和运行分开 编译代码在虚拟机中 运行…

BGP基础配置

EBGP是AS之间 IBGP是AS内 R1-R2是EBGP,R4-R5是EBGP R2-R3-R4是IBGP 第一步基础配置&#xff1a;IP地址 [r1-GigabitEthernet0/0/0]ip ad 12.0.0.1 24 [r1-LoopBack0]ip ad 1.1.1.1 32 [r2-GigabitEthernet0/0/0]ip ad 12.0.0.2 24 [r2-LoopBack0]ip ad 2.2.2.2 32 [r2-Loop…

【论文解读】在上下文中学习创建任务向量

一、简要介绍 大型语言模型&#xff08;LLMs&#xff09;中的上下文学习&#xff08;ICL&#xff09;已经成为一种强大的新的学习范式。然而&#xff0c;其潜在的机制仍未被很好地了解。特别是&#xff0c;将其映射到“标准”机器学习框架是具有挑战性的&#xff0c;在该框架中…

成功解决:AssertionError: Torch not compiled with CUDA enabled

在运行pycharm项目的时候&#xff0c;出现了以上的报错&#xff0c;主要可以归结于以下两个个方面&#xff1a; 1、没有安装GPU版本的pytorch&#xff0c;只是使用清华的镜像地址下载了CPU版本的pytorch 2、安装的CUDA和安装的pytorch的版本不相互对应 我使用 pip list 来…

Python超级详细的变量命名规则

Python 需要使用标识符给变量命名&#xff0c;其实标识符就是用于给程序中变量、类、方法命名的符号&#xff08;简单来说&#xff0c;标识符就是合法的名字&#xff09;。 Python 语言的标识符必须以字母、下画线&#xff08;_&#xff09;开头&#xff0c;后面可以跟任意数目…

知识蒸馏—原理+代码实战(Distillation CNN 和 Progressive Distillation Diffusion)

文章目录 1. Distillation 基本概念2. Distillation MNIST CNN分类代码实战3. Progressive Distillation Diffusion生成代码实战3.1 Progressive Distillation原理3.2 v-parameterization3.2 渐进蒸馏 cifar 代码实战 1. Distillation 基本概念 知识蒸馏被广泛的用于模型压缩和…

一文带你了解如何在Java中操作Redis

文章目录 前言一、 Redis客户端简介1. Redis客户端分类2. Spring 整合 Redis 的两种方式 二、 使用 Jedis 操作 Redis1. Jedis的maven坐标2. 使用Jedis操作Redis的步骤3. Jedis 操作 Redis 示例 三、 使用 Spring Data Redis 操作 Redis1. Spring Data Redis 的 maven 坐标2. s…

Open3D点云数据处理(一):VSCode配置python,并安装open3d教程

文章目录 1 python下载与安装1.1 python下载1.2 python安装1.3 验证python是否安装成功 2 VSCode下载与安装2.1 下载2.2 安装2.3 安装汉化插件2.4 vscode安装python扩展2.5 编写一个简单的python程序并运行2.6 在外部终端中打印运行结果&#xff08;不是必选的&#xff0c;不修…

丽晶酒店及度假村打造绮丽之境“美食实验室”中国市场首秀

于重庆丽晶酒店以艺术与美食的碰撞演绎“对比之美”&#xff0c;感官之华 2023年11月28日&#xff0c;中国上海 ——基于对当下消费趋势的敏锐洞察&#xff0c;洲际酒店集团旗下奢华品牌丽晶酒店及度假村近年来不断焕新&#xff0c;以崭新形象缔造现代奢华的旅居体验。作为丽晶…

TDL CDL信道模型

文章目录 一 TDL二 CDL三 CDL TDL区别 TDL&#xff1a;(Tapped Delay Line&#xff0c;抽头延迟线) CDL&#xff1a;(Clustered Delay Line&#xff0c;集群延迟线) 一 TDL 定义&#xff1a;由一组不同衰落系数和不同时延的抽头组成。全频率范围为&#xff1a;0.5GHz~100GHz&am…

第七节HarmonyOS UIAbility生命周期以及启动模式

一、UIAbility生命周期 为了实现多设备形态上的裁剪和多窗口的可扩展性&#xff0c;系统对组件管理和窗口管理进行了解耦。UIAbility的生命周期包括Create、Foreground、Background、Destroy四个状态&#xff0c;WindowStageCreate和WindowStageDestroy为窗口管理器&#xff08…

【软件测试面试】项目经验回答+面试技巧和方法汇总...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 1、测试面试时问你…

井盖倾斜怎么办?智能井盖传感器监测方法

井盖倾斜是一个紧迫的问题&#xff0c;如果不及时处理可能会导致道路安全性下降&#xff0c;进而增加车辆和行人发生意外的风险。为应对这一问题现已开发出智能井盖传感器&#xff0c;它可以持续监测井盖的状态&#xff0c;一旦发现倾斜等异常情况会立即发出警报。 在智慧城市的…

【unity实战】如何更加规范的创建各种Rogue-Lite(肉鸽)风格的物品和BUFF效果(附项目源码)

文章目录 前言定义基类实现不同的BUFF效果一、回血BUFF1. 简单的回血效果实现2. BUFF层数控制回血量 二、攻击附带火焰伤害三、治疗领域1. 简单的治疗领域实现2. 添加技能冷却时间 通过拾取物品获取对应的BUFF参考源码完结 前言 当创建各种Rogue-Lite&#xff08;肉鸽&#xf…

Linux基础项目开发1:量产工具——显示系统(二)

前言&#xff1a; 前面我们已经对这个项目的基本框架有了一个初步的了解与认识&#xff0c;要实现显示管理器与输入管理器&#xff0c;有输入有输出基本就实现这个项目的大部分功能了&#xff0c;首先我们先来做显示系统&#xff0c;对于上层系统为了让程序更好扩展&#xff0c…

策略模式实践

目录 前言 五个部分 名词解释 代码 controller层 HelloService接口 实现类 自定义注解 上下文 策略工厂 Java SPI配置 验证 前言 五个部分 接口、实现类、自定义注解、上下文、策略工厂 名词解释 自定义注解(方便后期增加实现类后灵活控制策略) 上下文(初始化…