当前位置: 首页 > news >正文

【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++】初步认识模板

&#x1f3d6;️作者&#xff1a;malloc不出对象 ⛺专栏&#xff1a;C的学习之路 &#x1f466;个人简介&#xff1a;一名双非本科院校大二在读的科班编程菜鸟&#xff0c;努力编程只为赶上各位大佬的步伐&#x1f648;&#x1f648; 目录 前言一、泛型编程二、函数模板2.1 函…...

Ansible 临时命令搭建安装仓库

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

phpstorm动态调试

首先在phpstudy搭建好网站&#xff0c;在管理拓展开启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.二叉树层序遍历 二叉树的层序遍历需要一个队列来帮助实现。 我们在队列中存储的是节点的地址&#xff0c;所以我们要对队列结构体的数据域重定义&#xff0c; 以上代码 从逻辑上来讲就是1入队&#xff0c;1出队&am…...

java八股文面试[JVM]——JVM内存结构

参考&#xff1a; JVM学习笔记&#xff08;一&#xff09;_卷心菜不卷Iris的博客-CSDN博客 JVM是运行在操作系统之上的&#xff0c;它与硬件没有直接的交互 JVM内存结构&#xff1a; 方法区&#xff1a;存储已被虚拟机加载的类元数据信息(元空间) 堆&#xff1a;存放对象实…...

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端口提供服务&#xf…...

【目标检测】理论篇(2)YOLOv3网络构架及其代码实现

网络构架图&#xff1a; 代码实现&#xff1a; import math from collections import OrderedDictimport torch.nn as nn#---------------------------------------------------------------------# # 残差结构 # 利用一个1x1卷积下降通道数&#xff0c;然后利用一个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、比例缩放&#xff08;Proportional Scaling&#xff09;2.3.2、HPA&#xff08;动态扩缩容&#xff09;2.3.2.1、需要先安装metrics-server2.3.2.2、配置hpa…...

IDEA项目实践——Element UI概述

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

Docker 容器学习笔记

Docker 容器学习笔记 容器的由来 早先&#xff0c;虚拟机通过操作系统实现相互隔离&#xff0c;保证应用程序在运行时相互独立&#xff0c;避免相互干扰。但是操作系统又笨又重&#xff0c;耗费资源严重&#xff1a; 容器技术只隔离应用程序的运行时环境但容器之间共享同一个…...

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

实验结论&#xff1a;在RAC环境中&#xff0c;最好修改参数sid* 安全可靠&#xff0c;因为暂时未明确知道哪些参数是默认全局修改&#xff0c;什么参数是默认单节点修改的&#xff0c;* 靠谱&#xff0c;不容易出问题 在RAC环境中&#xff0c;修改全局参数scopespfile生效时&am…...

【位运算进阶之----左移(<<)】

今天我们来谈谈左移这件事。 ❤️简单来说&#xff0c;对一个数左移就是在其的二进制表达末尾添0。左移一位添一个0&#xff0c;结果就是乘以2&#xff1b;左移两位添两个0&#xff0c;结果就乘以2 ^ 2&#xff1b;左移n位添n个0&#xff0c;结果就是乘以2 ^ n&#xff0c;小心…...

石油石化行业网络监控运维方案,全局态势感知,实时预警

石油石化行业是一个高科技密集型行业&#xff0c;投资巨大、人员众多&#xff0c;各产业价值链的关联度较高&#xff0c;大型石油石化企业实现了上中下游产业的一体化协同发展。随着工业4.0时代的来临&#xff0c;信息化和工业化融合&#xff0c;物联网、云计算等新技术的普及推…...

MyBatis 的关联关系配置 一对多,一对一,多对多 关系的映射处理

目录 一.关联关系配置的好处 二. 导入数据库表&#xff1a; 三. 一对多关系&#xff1a;-- 一个订单对应多个订单项 四.一对一关系&#xff1a;---一个订单项对应一个订单 五.多对多关系&#xff08;两个一对多&#xff09; 一.关联关系配置的好处 MyBatis是一…...

Diffusion Models for Image Restoration and Enhancement – A Comprehensive Survey

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

Springboot开发所遇问题(持续更新)

SpringBoot特征&#xff1a; 1. SpringBoot Starter&#xff1a;他将常用的依赖分组进行了整合&#xff0c;将其合并到一个依赖中&#xff0c;这样就可以一次性添加到项目的Maven或Gradle构建中。 2,使编码变得简单&#xff0c;SpringBoot采用 JavaConfig的方式对Spring进行配置…...

智能电视与win10电脑后续无法实现DLNA屏幕共享

问题背景&#xff1a; 我用的是TCL电视&#xff0c;但是并不是最新&#xff0c;打开的方式是U盘->电脑&#xff0c;各位看自己情况&#xff0c;很多问题都大概率是智能电视问题。 情景假设&#xff1a; 假设你已经完成原先智能电视该有的步骤&#xff0c;通过DLNA&#xf…...

如何可以管理监督员工工作微信?

自从微信管理系统研发上线之后&#xff0c;为了各企业带来了福音。 很多用户企业都是这样评论微信管理系统的&#xff1a;员工的所有微信聊天记录后台都可以清楚明了的看到&#xff0c;聊天记录都是永久保存的&#xff0c;不担心员工在手机上把聊天记录删除&#xff0c;杜绝员…...

【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…...

接口测试中缓存处理策略

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

云计算——弹性云计算器(ECS)

弹性云服务器&#xff1a;ECS 概述 云计算重构了ICT系统&#xff0c;云计算平台厂商推出使得厂家能够主要关注应用管理而非平台管理的云平台&#xff0c;包含如下主要概念。 ECS&#xff08;Elastic Cloud Server&#xff09;&#xff1a;即弹性云服务器&#xff0c;是云计算…...

C++:std::is_convertible

C++标志库中提供is_convertible,可以测试一种类型是否可以转换为另一只类型: template <class From, class To> struct is_convertible; 使用举例: #include <iostream> #include <string>using namespace std;struct A { }; struct B : A { };int main…...

React Native 开发环境搭建(全平台详解)

React Native 开发环境搭建&#xff08;全平台详解&#xff09; 在开始使用 React Native 开发移动应用之前&#xff0c;正确设置开发环境是至关重要的一步。本文将为你提供一份全面的指南&#xff0c;涵盖 macOS 和 Windows 平台的配置步骤&#xff0c;如何在 Android 和 iOS…...

2025年能源电力系统与流体力学国际会议 (EPSFD 2025)

2025年能源电力系统与流体力学国际会议&#xff08;EPSFD 2025&#xff09;将于本年度在美丽的杭州盛大召开。作为全球能源、电力系统以及流体力学领域的顶级盛会&#xff0c;EPSFD 2025旨在为来自世界各地的科学家、工程师和研究人员提供一个展示最新研究成果、分享实践经验及…...

3.3.1_1 检错编码(奇偶校验码)

从这节课开始&#xff0c;我们会探讨数据链路层的差错控制功能&#xff0c;差错控制功能的主要目标是要发现并且解决一个帧内部的位错误&#xff0c;我们需要使用特殊的编码技术去发现帧内部的位错误&#xff0c;当我们发现位错误之后&#xff0c;通常来说有两种解决方案。第一…...

聊聊 Pulsar:Producer 源码解析

一、前言 Apache Pulsar 是一个企业级的开源分布式消息传递平台&#xff0c;以其高性能、可扩展性和存储计算分离架构在消息队列和流处理领域独树一帜。在 Pulsar 的核心架构中&#xff0c;Producer&#xff08;生产者&#xff09; 是连接客户端应用与消息队列的第一步。生产者…...

Python爬虫(一):爬虫伪装

一、网站防爬机制概述 在当今互联网环境中&#xff0c;具有一定规模或盈利性质的网站几乎都实施了各种防爬措施。这些措施主要分为两大类&#xff1a; 身份验证机制&#xff1a;直接将未经授权的爬虫阻挡在外反爬技术体系&#xff1a;通过各种技术手段增加爬虫获取数据的难度…...

OpenLayers 分屏对比(地图联动)

注&#xff1a;当前使用的是 ol 5.3.0 版本&#xff0c;天地图使用的key请到天地图官网申请&#xff0c;并替换为自己的key 地图分屏对比在WebGIS开发中是很常见的功能&#xff0c;和卷帘图层不一样的是&#xff0c;分屏对比是在各个地图中添加相同或者不同的图层进行对比查看。…...

Linux离线(zip方式)安装docker

目录 基础信息操作系统信息docker信息 安装实例安装步骤示例 遇到的问题问题1&#xff1a;修改默认工作路径启动失败问题2 找不到对应组 基础信息 操作系统信息 OS版本&#xff1a;CentOS 7 64位 内核版本&#xff1a;3.10.0 相关命令&#xff1a; uname -rcat /etc/os-rele…...