【C++】初步认识模板
🏖️作者:@malloc不出对象
⛺专栏:C++的学习之路
👦个人简介:一名双非本科院校大二在读的科班编程菜鸟,努力编程只为赶上各位大佬的步伐🙈🙈
目录
- 前言
- 一、泛型编程
- 二、函数模板
- 2.1 函数模板的原理
- 2.2 函数模板的实例化
- 2.3 模板参数的匹配原则
- 三、类模板
- 3.1 类模板的实例化
- 3.2 类模板与模板类
前言
本篇文章我们讲解的是模板,它极大的节省了我们成本去构造多份差不多的代码,它是复用性很强的一种手段,下面就让我们初步认识一下模板吧。
一、泛型编程
如何实现一个通用的交换函数呢?我们可能会写出多个交换函数的版本:
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++中,也能够存在这样一个模具,通过给这个模具中填充不同材料(类型),来获得不同材料的铸件(即生成具体类型的代码),那将会节省许多时间。基于这样的原因,模板就被C++之父设计出来了。
泛型编程:编写与类型无关的通用代码,是代码复用的一种手段。模板是泛型编程的基础。
二、函数模板
函数模板概念:函数模板代表了一个函数家族,该函数模板与类型无关,在使用时被参数化,根据实参类型产生函数的特定类型版本。
函数模板格式
template<typename T1, typename T2,......,typename Tn>
,同样也可以使用template<class T1, class T2, ......, class Tn>
,关于这两种方式的区别我们后续再来谈。
返回值类型 函数名(参数列表){}
下面我们先来简单的看看它的用法:
#include <iostream>
using namespace std;//template<class T>
template<typename T>
void Swap(T& a, T& b)
{T tmp = a;a = b;b = tmp;
}int main()
{int a = 1, b = 2;Swap(a, b);cout << a << " " << b << endl;double c = 1.2, d = 3.4;Swap(c, d);cout << c << " " << d << endl;return 0;
}
从上述结果中我们发现利用函数模板的确实现了不同版本的交换函数,那么函数模板它到底是如何实现不同版本的交换函数的呢?我们调用函数时都是调用的函数模板吗?
2.1 函数模板的原理
函数模板是一个蓝图,它本身并不是函数,是编译器用使用方式产生特定具体类型函数的模具,所以其实模板就是将本来应该我们做的重复的事情交给了编译器去完成。
在编译器编译阶段,对于模板函数的使用,编译器需要根据传入的实参类型来推演生成对应类型的函数以供调用。比如:当用double类型使用函数模板时,编译器通过对实参类型的推演,将T确定为double类型,然后产生一份专门处理double类型的代码,对于其他数据类型也是如此。
我们通过反汇编来查看一下这其中的逻辑:
我们发现编译器根据传入的实参类型自动推演生成对应类型的函数,实际我们调用的也就是编译器自动生成的函数。
2.2 函数模板的实例化
函数模板实例化:用不同类型的参数使用函数模板时,称为函数模板的实例化。模板参数实例化分为:隐式实例化和显式实例化。
- 隐式实例化:让编译器根据实参推演模板参数的实际类型
#include <iostream>
using namespace std;template<class T>
T Add(const T& left, const T& right)
{return left + right;
}int main()
{int a1 = 10, a2 = 20;double d1 = 10.2, d2 = 20.3;// 实参传递给形参,自动推演模板类型cout << Add(a1, a2) << endl;cout << Add(d1, d2) << endl;return 0;
}
如果我们想传递两个类型不同的实参进行相加,是否能够达到目的呢?
从报错信息我们知道,此时我们的模板参数类型都是T,那么在传递实参时一个为int类型一个为double类型,此时模板参数类型就确定不了,编译器也不敢擅自做类型转换操作,那么我们该如何操作呢?
第一种方法就是进行强制类型转换,使模板参数类型保持一致,然后实例化出对应的函数模板:
注:上述的模板参数必须使用const修饰,因为强制类型转换会产生临时变量,临时变量具有常属性,实参具有常属性那么形参也要保持一致!!!
- 显式实例化:在函数名后的<>中指定模板参数的实际类型
指明模板参数的实际类型之后,如果类型不匹配,编译器会尝试进行隐式类型转换,如果无法转换成功编译器将会报错。
上述两个实参类型不同而模板参数类型是一致的,所以我们需要保持实参类型一致才能实例化出对应的函数,那么我觉得这个地方其实是有些别扭且不方便的,我们就是想让类型不一致的进行相加呢?
实际上我们的模板参数类型并不一定只有一种,这里我们可以使用两种模板参数类型来完成这个任务:
注:上述模板参数返回类型可以是T1也可以是T2,可以按照自己的需求来设计。
2.3 模板参数的匹配原则
- 一个非模板函数可以和一个同名的函数模板同时存在,而且该函数模板还可以被实例化为这个非模板函数
一个非模板函数可以和一个同名的函数模板同时存在,这其实也说明了函数模板实例化出来的函数与非模板函数是独立的两份不同的函数,它们的函数名经过修饰后也必定是不一致的。
- 对于非模板函数和同名函数模板,如果其他条件都相同,在调动时会优先调用非模板函数而不会从该模板产生出一个实例。如果模板可以产生一个具有更好匹配的函数, 那么将选择模板。
以下图为例,当非模板函数和一个同名的函数模板同时存在时,编译器会优先去考虑效率高的方式(最为合适的方式)。
- 模板函数不允许自动类型转换,但普通函数可以进行自动类型转换
三、类模板
类模板的定义格式
template<class T1, class T2, ..., class Tn> class 类模板名 { // 类内成员定义 };
我们先来看一个简单的例子:
typedef double STDataType;class Stack
{
public:Stack(size_t capacity = 0){_a = new T[5];_top = 0;_capacity = capacity;}~Stack(){delete[] _a;_capacity = _top = 0;}private:STDataType* _a;size_t _top;size_t _capacity;
};
利用typedef
我们就能实现多种数据类型的栈结构,增强了代码的维护性,但是如果我想要同时实现多份不同数据类型的栈结构呢?此时我们使用typedef
还能做到吗?答案是不行的,typedef
只适用于实现一份数据类型的栈结构,,那么我们想同时实现多份不同数据类型的栈结构就只能使用我们的类模板了。
3.1 类模板的实例化
类模板实例化与函数模板实例化不同,类模板实例化需要在类模板名字后跟<>,然后将实例化的类型放在<>中即可,类模板名字不是真正的类,而实例化的结果才是真正的类。
函数模板实参传递给形参时可以推演模板参数类型,而类模板是不确定的,在实例化对象调用构造函数初始化过程中,模板参数是不确定的。如上图在对象实例化时,调用构造函数此时我们的模板参数类型T无从得知。
注:类模板必须显式实例化!!!
3.2 类模板与模板类
类模板是一个类家族,模板类是通过类模板实例化的具体类。类模板的重点是模板表示的是一个模板,专门用于产生类的模子;模板类的重点是类,表示的是由一个模板生成而来的类,类模板中的虚拟类型参数指定成一个具体的数据类型参数就是模板类。更通俗一点来说,类模板可以看作是做蛋糕的模具,模板类可以看作是通过蛋糕模具做出来的蛋糕。
下面我们来看一个例子,来讲讲类模板的性质:
// 注意:Vector不是具体的类,是编译器根据被实例化的类型生成具体类的模具
template<class T>
class Vector
{
public :Vector(size_t capacity = 10): _pData(new T[capacity]), _size(0), _capacity(capacity){}// 使用析构函数演示:在类中声明,在类外定义。~Vector();void PushBack(const T& data);void PopBack();// ...size_t Size() {return _size;}T& operator[](size_t pos){assert(pos < _size);return _pData[pos];}
private:T* _pData;size_t _size;size_t _capacity;
};// 注意:类模板中函数放在类外进行定义时,需要加模板参数列表
template <class T>
Vector<T>::~Vector()
{if(_pData)delete[] _pData;_size = _capacity = 0;
}int main()
{Vector<int> v1; // Vector类名,Vector<int>才是类型Vector<double> v2;return 0;
}
上述例子中Vector
就是类模板,Vector<int>
、Vector<double>
都是模板类。
类模板的性质:
1.如果一个类被声明为类模板,那么其中的所有成员函数都是模板函数。
2.类模板中函数放在类外进行定义时,需要加模板参数列表。
3.模板的定义与声明不能分离!!!(这点我们以后再来探讨)。
本篇文章的内容就到这里了,如果文章有任何疑问或者错处欢迎大家评论区相互交流orz~🙈🙈
相关文章:

【C++】初步认识模板
🏖️作者:malloc不出对象 ⛺专栏:C的学习之路 👦个人简介:一名双非本科院校大二在读的科班编程菜鸟,努力编程只为赶上各位大佬的步伐🙈🙈 目录 前言一、泛型编程二、函数模板2.1 函…...

Ansible 临时命令搭建安装仓库
创建一个名为/ansible/yum.sh 的 shell 脚本,该脚本将使用 Ansible 临时命令在各个受管节点上安装 yum 存储库. 存储库1: 存储库的名称为 EX294_BASE 描述为 EX294 base software 基础 URL 为 http://content/rhel8.0/x86_64/dvd/BaseOS GPG 签名检查为…...

phpstorm动态调试
首先在phpstudy搭建好网站,在管理拓展开启xdebug拓展 查看php.ini配置已经更改 需要增添修改一下设置 [Xdebug] zend_extensionD:/phpstudy_pro/Extensions/php/php5.6.9nts/ext/php_xdebug.dll xdebug.collect_params1 xdebug.collect_return1 xdebug.auto_trace…...

二叉树的层序遍历及完全二叉树的判断
文章目录 1.二叉树层序遍历 2.完全二叉树的判断 文章内容 1.二叉树层序遍历 二叉树的层序遍历需要一个队列来帮助实现。 我们在队列中存储的是节点的地址,所以我们要对队列结构体的数据域重定义, 以上代码 从逻辑上来讲就是1入队,1出队&am…...

java八股文面试[JVM]——JVM内存结构
参考: JVM学习笔记(一)_卷心菜不卷Iris的博客-CSDN博客 JVM是运行在操作系统之上的,它与硬件没有直接的交互 JVM内存结构: 方法区:存储已被虚拟机加载的类元数据信息(元空间) 堆:存放对象实…...

Kafka基本使用
查看Kafka的进程是否在运行 #命令行终端中运行如下命令 ps -ef | grep kafkafind / -iname kafka-server-start.shcd /usr/local/kafka/bin/#启动kafka ./kafka-server-start.sh -daemon /usr/local/kafka/config/server.propertiesKafka默认使用9092端口提供服务…...

【目标检测】理论篇(2)YOLOv3网络构架及其代码实现
网络构架图: 代码实现: import math from collections import OrderedDictimport torch.nn as nn#---------------------------------------------------------------------# # 残差结构 # 利用一个1x1卷积下降通道数,然后利用一个3x3卷…...

k8s之工作负载、Deployment、DaemonSet、StatefulSet、Job、CronJob及GC
文章目录 1、工作负载1.1、定义1.2、分类 2、Deployment2.1、定义2.2、Deployment创建2.3、Deployment 更新机制2.3.1、比例缩放(Proportional Scaling)2.3.2、HPA(动态扩缩容)2.3.2.1、需要先安装metrics-server2.3.2.2、配置hpa…...

IDEA项目实践——Element UI概述
系列文章目录 IDEA项目实践——JavaWeb简介以及Servlet编程实战 IDEA项目实践——Spring当中的切面AOP IDEA项目实践——Spring框架简介,以及IOC注解 IDEA项目实践——动态SQL、关系映射、注解开发 IDEWA项目实践——mybatis的一些基本原理以及案例 文章目录 …...

Docker 容器学习笔记
Docker 容器学习笔记 容器的由来 早先,虚拟机通过操作系统实现相互隔离,保证应用程序在运行时相互独立,避免相互干扰。但是操作系统又笨又重,耗费资源严重: 容器技术只隔离应用程序的运行时环境但容器之间共享同一个…...
Day03-vue基础
Day03-vue基础 一 列表渲染 v-for这个指令可以实现列表渲染 1 数组 <ul><!-- v-for遍历的时候,key必须赋唯一值第一个参数是数组元素,第二个参数是元素下标--><li v-for="(item,index) in [1,3,5,7]" :key="item">{{item}}--{{index}…...
RAC sid=‘*‘ 最好加上 v$system_parameter
实验结论:在RAC环境中,最好修改参数sid* 安全可靠,因为暂时未明确知道哪些参数是默认全局修改,什么参数是默认单节点修改的,* 靠谱,不容易出问题 在RAC环境中,修改全局参数scopespfile生效时&am…...

【位运算进阶之----左移(<<)】
今天我们来谈谈左移这件事。 ❤️简单来说,对一个数左移就是在其的二进制表达末尾添0。左移一位添一个0,结果就是乘以2;左移两位添两个0,结果就乘以2 ^ 2;左移n位添n个0,结果就是乘以2 ^ n,小心…...

石油石化行业网络监控运维方案,全局态势感知,实时预警
石油石化行业是一个高科技密集型行业,投资巨大、人员众多,各产业价值链的关联度较高,大型石油石化企业实现了上中下游产业的一体化协同发展。随着工业4.0时代的来临,信息化和工业化融合,物联网、云计算等新技术的普及推…...

MyBatis 的关联关系配置 一对多,一对一,多对多 关系的映射处理
目录 一.关联关系配置的好处 二. 导入数据库表: 三. 一对多关系:-- 一个订单对应多个订单项 四.一对一关系:---一个订单项对应一个订单 五.多对多关系(两个一对多) 一.关联关系配置的好处 MyBatis是一…...

Diffusion Models for Image Restoration and Enhancement – A Comprehensive Survey
图像恢复与增强的扩散模型综述 论文链接:https://arxiv.org/abs/2308.09388 项目地址:https://github.com/lixinustc/Awesome-diffusion-model-for-image-processing/ Abstract 图像恢复(IR)一直是低水平视觉领域不可或缺的一项具有挑战性的任务&…...

Springboot开发所遇问题(持续更新)
SpringBoot特征: 1. SpringBoot Starter:他将常用的依赖分组进行了整合,将其合并到一个依赖中,这样就可以一次性添加到项目的Maven或Gradle构建中。 2,使编码变得简单,SpringBoot采用 JavaConfig的方式对Spring进行配置…...

智能电视与win10电脑后续无法实现DLNA屏幕共享
问题背景: 我用的是TCL电视,但是并不是最新,打开的方式是U盘->电脑,各位看自己情况,很多问题都大概率是智能电视问题。 情景假设: 假设你已经完成原先智能电视该有的步骤,通过DLNA…...

如何可以管理监督员工工作微信?
自从微信管理系统研发上线之后,为了各企业带来了福音。 很多用户企业都是这样评论微信管理系统的:员工的所有微信聊天记录后台都可以清楚明了的看到,聊天记录都是永久保存的,不担心员工在手机上把聊天记录删除,杜绝员…...
【Django】如何转化已有的数据表到Django模型--20230823
初步生成model.py $ python manage.py inspectdb $ python manage.py inspectdb > models.py python manage.py inspectdb # This is an auto-generated Django model module. # Youll have to do the following manually to clean this up: # * Rearrange models order…...

接口测试中缓存处理策略
在接口测试中,缓存处理策略是一个关键环节,直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性,避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明: 一、缓存处理的核…...

springboot 百货中心供应链管理系统小程序
一、前言 随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱,百货中心供应链管理系统被用户普遍使用,为方…...

Kafka入门-生产者
生产者 生产者发送流程: 延迟时间为0ms时,也就意味着每当有数据就会直接发送 异步发送API 异步发送和同步发送的不同在于:异步发送不需要等待结果,同步发送必须等待结果才能进行下一步发送。 普通异步发送 首先导入所需的k…...
LCTF液晶可调谐滤波器在多光谱相机捕捉无人机目标检测中的作用
中达瑞和自2005年成立以来,一直在光谱成像领域深度钻研和发展,始终致力于研发高性能、高可靠性的光谱成像相机,为科研院校提供更优的产品和服务。在《低空背景下无人机目标的光谱特征研究及目标检测应用》这篇论文中提到中达瑞和 LCTF 作为多…...
Android屏幕刷新率与FPS(Frames Per Second) 120hz
Android屏幕刷新率与FPS(Frames Per Second) 120hz 屏幕刷新率是屏幕每秒钟刷新显示内容的次数,单位是赫兹(Hz)。 60Hz 屏幕:每秒刷新 60 次,每次刷新间隔约 16.67ms 90Hz 屏幕:每秒刷新 90 次,…...

ubuntu中安装conda的后遗症
缘由: 在编译rk3588的sdk时,遇到编译buildroot失败,提示如下: 提示缺失expect,但是实测相关工具是在的,如下显示: 然后查找借助各个ai工具,重新安装相关的工具,依然无解。 解决&am…...
深入解析 ReentrantLock:原理、公平锁与非公平锁的较量
ReentrantLock 是 Java 中 java.util.concurrent.locks 包下的一个重要类,用于实现线程同步,支持可重入性,并且可以选择公平锁或非公平锁的实现方式。下面将详细介绍 ReentrantLock 的实现原理以及公平锁和非公平锁的区别。 ReentrantLock 实现原理 基本架构 ReentrantLo…...

RKNN开发环境搭建2-RKNN Model Zoo 环境搭建
目录 1.简介2.环境搭建2.1 启动 docker 环境2.2 安装依赖工具2.3 下载 RKNN Model Zoo2.4 RKNN模型转化2.5编译C++1.简介 RKNN Model Zoo基于 RKNPU SDK 工具链开发, 提供了目前主流算法的部署例程. 例程包含导出RKNN模型, 使用 Python API, CAPI 推理 RKNN 模型的流程. 本…...
WEB3全栈开发——面试专业技能点P8DevOps / 区块链部署
一、Hardhat / Foundry 进行合约部署 概念介绍 Hardhat 和 Foundry 都是以太坊智能合约开发的工具套件,支持合约的编译、测试和部署。 它们允许开发者在本地或测试网络快速开发智能合约,并部署到链上(测试网或主网)。 部署过程…...

python学习day39
图像数据与显存 知识点回顾 1.图像数据的格式:灰度和彩色数据 2.模型的定义 3.显存占用的4种地方 a.模型参数梯度参数 b.优化器参数 c.数据批量所占显存 d.神经元输出中间状态 4.batchisize和训练的关系 import torch import torchvision import torch.nn as nn imp…...