【C++深入浅出】模版初识
目录
一. 前言
二. 泛型编程
三. 函数模版
3.1 函数模版的概念
3.2 函数模版的格式
3.3 函数模版的原理
3.4 函数模板的实例化
3.5 模板参数的匹配原则
四. 类模版
4.1 类模版的定义
4.2 类模版的实例化
一. 前言
本期我们要介绍的是C++的又一大重要功能----模版。通过模版,我们可以很轻松的进行泛型编程,大大简化我们编程时的代码。
本文的目标是让读者对模版有一定程度上的了解,以便后续STL的学习,对于模版更深层次的内容,我们放到以后再进行拓展。
话不多说,开启我们今日的学习叭
二. 泛型编程
假如现在有个需求,要求我们实现一个swap函数用于数据的交换,数据类型可能是整形、浮点型、字符型等等,通过我们之前学习的知识,你会如何进行实现呢?
聪明的你可能会这样实现
//利用函数重载实现三种不同类型的swap函数
void Swap(int& x, int& y) //引用传参
{int tmp = x;x = y;y = tmp;
}void Swap(double& x, double& y)
{double tmp = x;x = y;y = tmp;
}void Swap(char& x, char& y)
{double tmp = x;x = y;y = tmp;
}
是的,上面的代码确实实现了我们的需求,但还是存在一些不足,体现在如下两个方面:
- 几个重载函数仅仅是类型不同,代码逻辑都是一样的,代码的复用率比较低。每当有新类型出现时,用户就需要再自行添加一个函数
- 代码的可维护性比较低,一个出错可能所有的重载均出错(想必你写的时候也是CV的叭
)
依照以上两点不足,那我们能不能只给编译器提供一个模板,到时候让编译器根据我们指定的类型自动生成所需要的函数呢?
答案是有的。C++为我们提供了模版的概念,支持我们进行泛型编程。模版就好比一个模具,我们可以从倒入任何种类(类型)的东西到模具中,最终都会形成我们想要的对应图案(即生成相应的代码)
泛型编程:编写与类型无关的通用代码,是代码复用的一种手段。模版是泛型编程的基础。模板分为函数模版和类模版:
三. 函数模版
3.1 函数模版的概念
一个函数模版代表了一个函数的整个家族。该函数模板与类型无关,在使用时被会被实例化,根据实参类型实例化出特定类型的函数版本。
3.2 函数模版的格式
函数模版的定义格式如下所示:
template<typename T1, typename T2,......,typename Tn>
返回值类型 函数名(参数列表){}
例如,我们将上面的swap函数定义为函数模版如下:
template<typename T> //T是类型名
void swap(T& x, T& y)
{T tmp = x;x = y;y = tmp;
}
其中,typename是定义模版参数的关键字,也可以使用class关键字替代。
3.3 函数模版的原理
我们可以把函数模板看做一个蓝图,它本身并不是一个函数,而是编译器根据调用函数时传入实参产生特定类型函数的一个模具。
例如,当我们给swap函数传入double类型的参数,编译器就会自动根据模板生成一份参数为double类型的swap函数;而如果我们传入的是int类型的参数,编译器就会生成一份参数为int类型的swap函数。
由此可见,模板就是将本来应该我们做的重复的事情交给了编译器去做,将手动敲出来的代码变为编译器自动生成。
在编译器编译阶段时,对于模板函数的使用,编译器需要根据传入的实参类型来推演T并生成对应类型的函数以供调用。比如:当double类型数据使用函数模板时,编译器通过对实参类型的推演,将T确定为double类型,然后产生一份专门处理double类型的代码,对于其余类型也是如此。
3.4 函数模板的实例化
当不同类型的参数使用函数模板时,编译器根据模版为这个类型生成一份函数代码,这个过程称为函数模板的实例化。模板参数实例化分为:隐式实例化和显式实例化。
隐式实例化
即编译器根据实参的类型自动推导模版参数的类型:
template<class T>
T Add(const T& left, const T& right)
{return left + right;
}int main()
{double d1 = 2.0;double d2 = 5.0;Add(d1, d2); //d1和d2是double类型的,编译时编译器推导T为double类型,实例化double类型的Add函数int i1 = 0;Add(i1, d2); //此处编译会报错,因为Add只有一个模板参数T,而实参有两种类型,编译器推导时不知道将T推导为int还是doublereturn 0;
}
可以看到,编译器进行隐式实例化也不是胡乱实例化的,必须要有唯一的结果编译器才会进行实例化,编译器一般不会进行类型转换操作,因为一旦转化出问题,编译器就需要背黑锅。
要解决上面Add函数的问题,我们有两个解决方法:
1、用户自行进行强制类型转换
Add(i1, (int)d2); //将两个实参都变为int类型Add((double)i1, d2); //将两个实参都变为double类型
2、使用下面的显式实例化
显式实例化
调用函数时我们可以在函数名后使用<>来指定模板参数的实际类型,如下:
int main()
{int a = 10;double b = 20.0;double c = 30.0;// 显式实例化Add<int>(a, b); //显式指定T实例化为intAdd<double>(b, c); //显式指定T实例化为doublereturn 0;
}
当实例化的函数形参和实参的类型不匹配时,编译器就会尝试进行隐式类型转换,如果无法转换成功编译器将会报错
template<typename T>
void Swap(T& x, T& y)
{T tmp = x;x = y;y = tmp;
}
int main()
{int i = 10;double d = 10.0;Swap<int>(i, d); //尽管我们显式进行实例化了,但这里编译器仍然会报错,原因是形参和实参无法进行隐式类型转换return 0;
}
3.5 模板参数的匹配原则
1、 一个非模板函数可以和一个同名的函数模板同时存在,并且该函数模板还可以被实例化为这个非模板函数。换句话说,我们既可以使用非模板函数,也可以使用编译器特化的模板函数。
//Swap函数模板
template<class T>
void Swap(T& x, T& y)
{T tmp = x;x = y;y = tmp;
}//处理int类型的Swap函数
void Swap(int& x, int& y)
{int tmp = x;x = y;y = tmp;
}int main()
{int a = 10, b = 20;Swap(a, b); //使用下面专门处理int的Swap函数Swap<int>(a, b); //使用上面编译器特化后的Swap函数return 0;
}
2、对于非模板函数和同名函数模板,调用时会调用最匹配的那个函数。在其他条件相同的情况下,编译器会优先调用非模板函数。如果模板可以产生一个更匹配的函数,则将选择模板。
//Add函数模板
template<class T1,class T2>
T1 Add(T1 x, T2 y)
{return x + y;
}//处理int类型的Add函数
int Add(int x, int y)
{return x + y;
}int main()
{Add(10, 20); //虽然可以通过模板生成<int,int>的模板函数,但编译器并不会去生成,而是去调用已经存在的非模板函数Add(10, 20.0); //通过函数模板生成的函数会更加匹配,故调用函数模版生成的Add函数return 0;
}
3、模板函数不允许自动类型转换,但普通函数可以进行自动类型转换
//Add函数模板
template<class T>
T Add(T x, T y)
{return x + y;
}int Swap(int x, int y)
{int tmp = x;x = y;y = tmp;
}int main()
{int a = 20;double d = 10.0;Add(d, a); //这里会报错,Add是模板函数,不支持自动类型转换Swap(d, a); //这里编译通过,d会发生隐式类型转换为int类型return 0;
}
四. 类模版
4.1 类模版的定义
在C++中,除了普通函数支持模版,类也支持模版,我们把这样的类称为类模版。下面给出一个类模版的定义格式:
template<class T1, class T2, ..., class Tn> //模板参数
class 类模板名
{// 类内成员定义
};
下面我们用vector类来演示一下类模板的定义方法以及注意事项(只是演示噢,并不是真正的vector模拟实现)
//vector类
template<class T>
class Vector
{
public://构造时使用new动态申请空间Vector(int capacity = 10):_p(new T[capacity])_capacity(capacity),_size(0){}void puch_back(const T& x); //尾插void pop_back(); //尾删~Vector(); //析构时需要调用delete释放空间
private:T* _p;int _capacity;int _size;
};//在类模板外定义成员函数,需要加上模版参数列表
template<class T>
Vector<T>::~Vector()
{if (_p != nullptr){delete[] _p;_capacity = _size = 0;}
}
值得注意的是:和模板函数一样,类模板并不是一个具体的类,它只是一个模具,只有进行实例化后才会变成一个具体的类。
4.2 类模版的实例化
与函数模板实例化不同,类模板不支持隐式类型推导,类模板实例化需要在类模板名字后用<>显式指定模板参数的实际类型。注意:类模板的名字不是真正的类,只有指定类型实例化后才是真正的类。
//模板的实例化
int main()
{//Vector是类名,Vector<int>才是类型名,才能用来定义对象Vector<int> vi(20); //用参数为int的Vector类定义对象Vector<double> vd(10); //用参数为double的Vector类定义对象return 0;
}
类模版经过类型实例化后得到的类我们称作模板类,通过模板类我们就可以定义许多相应的类对象,三者的关系可用下图表示:
以上,就是本期的全部内容啦🌸
制作不易,能否点个赞再走呢🙏
相关文章:

【C++深入浅出】模版初识
目录 一. 前言 二. 泛型编程 三. 函数模版 3.1 函数模版的概念 3.2 函数模版的格式 3.3 函数模版的原理 3.4 函数模板的实例化 3.5 模板参数的匹配原则 四. 类模版 4.1 类模版的定义 4.2 类模版的实例化 一. 前言 本期我们要介绍的是C的又一大重要功能----模版。通…...
系统架构设计师-第18章-安全架构设计理论与实践-软考学习笔记
安全架构概述 信息的可用性、元略性、机密性、可控性和不可抵赖性等安全保障显得尤为重要,而满足这些诉求,离不开好的架构设计. 信息安全面临的威胁 常见的安全威胁有以下几种. (1)信息泄露 (2) 破坏信息的元整性: 数据被非授极地进行增删、修改成破坏…...
2023年吉安市“振兴杯”职业技能大赛网络安全项目样题
2023年吉安市“振兴杯”职业技能大赛 网络安全项目样题 需要竞赛环境可私信博主 赛题说明 一、竞赛项目简介 竞赛共分为:A.基础设施设置与安全加固;B.网络安全事件响应、数字取证调查和应用安全;C.CTF夺旗-攻击;D.CTF夺旗-防御等四…...

python爬虫selenium和ddddocr使用
python爬虫selenium和ddddocr使用 selenium使用 selenium实际上是web自动化测试工具,能够通过代码完全模拟人使用浏览器自动访问目标站点并操作来进行web测试。 通过pythonselenium结合来实现爬虫十分巧妙。 由于是模拟人的点击来操作,所以实际上被反…...
【vim 学习系列文章 12 -- vimrc 那点事】
文章目录 系统级及本地 vimrc 文件设置 vimrc 的路径 系统级及本地 vimrc 文件 当 Vim 启动时,编辑器会去搜索一个系统级的 vimrc 文件来进行系统范围内的默认初始化工作。 这个文件通常在你系统里 $VIM/vimrc 的路径下,如果没在那里,那你可…...
spring.factories介绍
spring.factories 是 Spring Framework 中的一个配置文件,它用于自动装配和加载 Spring 应用程序中的各种组件。该文件位于 META-INF/spring.factories,通常位于 JAR 文件的资源路径下。 spring.factories 文件采用键值对的形式,每个键代表一…...

业务设计——用户敏感信息展示脱敏及其反脱敏
业务需求 将用户敏感信息脱敏展示到前端是出于保护用户隐私和信息安全的考虑。 敏感信息包括但不限于手机号码、身份证号、银行卡号等,这些信息泄露可能导致用户个人信息的滥用、身份盗用等严重问题。脱敏是一种常用的保护用户隐私的方式,它的目的是减少…...

Hadoop分布式安装
首先准备好三台服务器或者虚拟机,我本机安装了三个虚拟机,安装虚拟机的步骤参考我之前的一篇 virtualBox虚拟机安装多个主机访问虚拟机虚拟机访问外网配置-CSDN博客 jdk安装 参考文档:Linux 环境下安装JDK1.8并配置环境变量_linux安装jdk1.8并…...
Python——PyQt5以及Pycharm相关配置
PyQt5目录 常见的GUI框架一、安装pyqt5pip install pyqt5pip install pyqt5-tools二、Qt Designer三、在PyCharm中配置相关toolQtDisigner配置PyUIC配置PyRCC配置常见的GUI框架 Tkinter:Python内置的GUI框架,使用TCL实现,Python中内嵌了TCL解释器,使用它的时候不用安装额外…...

java集成海康预览抓图出现内存一直上涨问题
求助:在java 中集成海康sdk后批量抓图出现内存上涨问题,不论是预览后不关闭继续预览,还是预览后关闭预览,然后重新预览都没有解决这个问题(抓图正常),尝试使用第三方解码器ffmpeg来进行解码&…...

Spring Boot 使用 Disruptor 做内部高性能消息队列
这里写自定义目录标题 一 、背景二 、Disruptor介绍三 、Disruptor 的核心概念3.1 Ring Buffer3.2 Sequence Disruptor3.3 Sequencer3.4 Sequence Barrier3.5 Wait Strategy3.6 Event3.7 EventProcessor3.8 EventHandler3.9 Producer 四、案例-demo五、总结 一 、背景 工作中遇…...

一、灵动mm32单片机_开发环境的搭建(Keil)
1、安装Keil MDK。 略。 2、安装芯片对应的Pack包。 (1)这里以MM32F0130单片机为例。 (2)进入灵动微电子官网。上海灵动微电子股份有限公司 (3)点击“支持”→“KEILPacl”。 (3)点击下载Pack包。 (4)下载后,解压下载的压缩包,找到对应的Pack包&…...

【5G PHY】5G SS/PBCH块介绍(二)
博主未授权任何人或组织机构转载博主任何原创文章,感谢各位对原创的支持! 博主链接 本人就职于国际知名终端厂商,负责modem芯片研发。 在5G早期负责终端数据业务层、核心网相关的开发工作,目前牵头6G算力网络技术标准研究。 博客…...

简单而高效:使用PHP爬虫从网易音乐获取音频的方法
概述 网易音乐是一个流行的在线音乐平台,提供了海量的音乐资源和服务。如果你想从网易音乐下载音频文件,你可能会遇到一些困难,因为网易音乐对其音频资源进行了加密和防盗链的处理。本文将介绍一种使用PHP爬虫从网易音乐获取音频的方法&…...

渗透测试工具-sqlmap使用
sqlmap是一个开源渗透测试的自动化工具,可以自动检测和利用SQL注入漏洞并接管数据库服务器。它配备了一个强大的检测引擎,许多用于终极渗透测试的利基功能,以及广泛的开关,包括数据库指纹识别、从数据库中获取数据、访问底层文件系…...
C# WPF: Imag图片填充方式有哪些?
C#和WPF中的图像填充方式 在WPF中,你可以使用Image控件来显示图像,并使用不同的填充方式来控制图像在控件中的显示方式。以下是一些常见的图像填充方式: Stretch(拉伸):这是默认的填充方式,它…...

uniapp开发小程序—根据生日日期计算年龄 周岁
0、需求 在UniApp开发小程序中,将接口返回的出生日期转化为年龄;判断接口返回的年龄是否是周岁 可以使用JavaScript的日期处理方法来实现。 一、第一种方式(示例代码): //javascript // 假设接口返回的年龄为生日的…...

windows下基于vscode的ssh服务远程连接ubuntu服务器
Ubuntu端配置 1.确保ubuntu端已启用ssh服务 首先,安装ssh服务 sudo apt-get install openssh-server 安装后,打开ssh服务 sudo service ssh start 如果显示有sshd就说明成功了。 判断是否成功打开 ps -e|grep ssh 同时也可以通过如下方式确保ss…...

OpenCV学习(二)——OpenCV中绘图功能
2. OpenCV中绘图功能2.1 画线2.2 画矩形2.3 画圆2.4 画多边形2.5 添加文本 2. OpenCV中绘图功能 绘图可以实现画线、画矩形、画圆、画多边形和添加文本等操作。 import cv2 import numpy as np# 读取图像 img cv2.imread(lena.jpg)# 画直线 cv2.line(img, (0, 0), (512, 512…...

业务架构、应用架构、技术架构、数据架构
架构规划的重要性 如果没有进行合理的架构规划,将会引发一系列的问题。为了避免这些问题的发生,企业需要进行业务架构、应用架构、技术架构和数据架构的全面规划和设计,以构建一个清晰、可持续发展的企业架构。 https://www.zhihu.com/que…...
Ubuntu系统下交叉编译openssl
一、参考资料 OpenSSL&&libcurl库的交叉编译 - hesetone - 博客园 二、准备工作 1. 编译环境 宿主机:Ubuntu 20.04.6 LTSHost:ARM32位交叉编译器:arm-linux-gnueabihf-gcc-11.1.0 2. 设置交叉编译工具链 在交叉编译之前&#x…...

【JavaEE】-- HTTP
1. HTTP是什么? HTTP(全称为"超文本传输协议")是一种应用非常广泛的应用层协议,HTTP是基于TCP协议的一种应用层协议。 应用层协议:是计算机网络协议栈中最高层的协议,它定义了运行在不同主机上…...

Unity3D中Gfx.WaitForPresent优化方案
前言 在Unity中,Gfx.WaitForPresent占用CPU过高通常表示主线程在等待GPU完成渲染(即CPU被阻塞),这表明存在GPU瓶颈或垂直同步/帧率设置问题。以下是系统的优化方案: 对惹,这里有一个游戏开发交流小组&…...

UE5 学习系列(三)创建和移动物体
这篇博客是该系列的第三篇,是在之前两篇博客的基础上展开,主要介绍如何在操作界面中创建和拖动物体,这篇博客跟随的视频链接如下: B 站视频:s03-创建和移动物体 如果你不打算开之前的博客并且对UE5 比较熟的话按照以…...

LeetCode - 394. 字符串解码
题目 394. 字符串解码 - 力扣(LeetCode) 思路 使用两个栈:一个存储重复次数,一个存储字符串 遍历输入字符串: 数字处理:遇到数字时,累积计算重复次数左括号处理:保存当前状态&a…...
Auto-Coder使用GPT-4o完成:在用TabPFN这个模型构建一个预测未来3天涨跌的分类任务
通过akshare库,获取股票数据,并生成TabPFN这个模型 可以识别、处理的格式,写一个完整的预处理示例,并构建一个预测未来 3 天股价涨跌的分类任务 用TabPFN这个模型构建一个预测未来 3 天股价涨跌的分类任务,进行预测并输…...
postgresql|数据库|只读用户的创建和删除(备忘)
CREATE USER read_only WITH PASSWORD 密码 -- 连接到xxx数据库 \c xxx -- 授予对xxx数据库的只读权限 GRANT CONNECT ON DATABASE xxx TO read_only; GRANT USAGE ON SCHEMA public TO read_only; GRANT SELECT ON ALL TABLES IN SCHEMA public TO read_only; GRANT EXECUTE O…...

【JavaWeb】Docker项目部署
引言 之前学习了Linux操作系统的常见命令,在Linux上安装软件,以及如何在Linux上部署一个单体项目,大多数同学都会有相同的感受,那就是麻烦。 核心体现在三点: 命令太多了,记不住 软件安装包名字复杂&…...

Maven 概述、安装、配置、仓库、私服详解
目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...
CSS设置元素的宽度根据其内容自动调整
width: fit-content 是 CSS 中的一个属性值,用于设置元素的宽度根据其内容自动调整,确保宽度刚好容纳内容而不会超出。 效果对比 默认情况(width: auto): 块级元素(如 <div>)会占满父容器…...