C++模版(基础)
目录
C++泛型编程思想
C++模版
模版介绍
模版使用
函数模版
函数模版基础语法
函数模版原理
函数模版实例化
模版参数匹配规则
类模版
类模版基础语法
C++泛型编程思想
泛型编程:编写与类型无关的通用代码,是代码复用的一种手段。
模板是泛型编程的基础。
虽然可以直接使用函数重载来解决不同类型的问题,但是使用函数重载会出现可能不好的地方
- 重载的函数仅仅是类型不同,代码复用率比较低,只要有新类型出现时,就需要用户自己增加对应的函数
- 代码的可维护性比较低,一个出错可能所有的重载均出错
C++模版
模版介绍
在C语言中,当需要交换两个变量的数据时需要考虑到不同类型,例如下面的代码
#define _CRT_SECURE_NO_WARNINGS 1#include <stdio.h>//交换int类型数据
void swap_int(int* num1, int* num2)
{int tmp = *num1;*num1 = *num2;*num2 = tmp;
}//交换double类型的数据
void swap_double(double* num1, double* num2)
{double tmp = *num1;*num1 = *num2;*num2 = tmp;
}int main()
{int num1 = 1, num2 = 2;printf("num1=%d num2=%d\n", num1, num2);swap_int(&num1, &num2);printf("num1=%d num2=%d\n", num1, num2);double num3 = 4.1, num4 = 4.5;printf("num3=%.1f num4=%.1f\n", num3, num4);swap_double(&num3, &num4);printf("num3=%.1f num4=%.1f\n", num3, num4);return 0;
}
输出结果:
num1=1 num2=2
num1=2 num2=1
num3=4.1 num4=4.5
num3=4.5 num4=4.1
在上面的C语言代码中,当需要交换int
类型的数据时需要int
类型交换函数,需要double
类型的数据时需要double
类型的交换函数,但是这两个函数除了类型不同以外其他代码都一样,增加了工作量,并且因为C语言不支持函数重载,所以两个交换函数的函数名不能相同
为了解决上面的问题,C++中提出了一种模版函数,如下面代码
#include <iostream>
using namespace std;template<class T>
void Swap(T& num1, T& num2)
{T tmp = num1;num1 = num2;num2 = tmp;
}int main()
{int num1 = 1, num2 = 2;printf("num1=%d num2=%d\n", num1, num2);Swap(num1, num2);printf("num1=%d num2=%d\n", num1, num2);double num3 = 4.1, num4 = 4.5;printf("num3=%.1f num4=%.1f\n", num3, num4);Swap(num3, num4);printf("num3=%.1f num4=%.1f\n", num3, num4);return 0;
}
输出结果:
num1=1 num2=2
num1=2 num2=1
num3=4.1 num4=4.5
num3=4.5 num4=4.1
在上面的代码中,将Swap
函数作为一种模版,当调用Swap
函数时,根据传入的参数类型自动实例化函数从而完成函数执行
模版使用
函数模版
函数模版基础语法
template<typename name1, typename name2, ...>
函数返回类型 函数名(形式参数)
{//函数体
}//typename也可以用class代替,但是不可以用struct
在C++中,使用template
关键字创建模版,使用<>
包裹函数体中需要使用到类型,typename name1
用于指代的类型,在函数调用时自动匹配类型,默认不会隐式类型转换,模版下方正常写函数即可
📌
模版函数的下方也可以写其他普通函数
#include <iostream>
using namespace std;//模版
template<class T>
void Swap(T& num1, T& num2)
{T tmp = num1;num1 = num2;num2 = tmp;
}//普通函数
int add(const int num1, const int num2)
{return num1 + num2;
}int main()
{int num1 = 1, num2 = 2;printf("num1=%d num2=%d\n", num1, num2);Swap(num1, num2);printf("num1=%d num2=%d\n", num1, num2);double num3 = 4.1, num4 = 4.5;printf("num3=%.1f num4=%.1f\n", num3, num4);Swap(num3, num4);printf("num3=%.1f num4=%.1f\n", num3, num4);cout << add(num1, num2) << endl;//可以正常使用return 0;
}
输出结果:
num1=1 num2=2
num1=2 num2=1
num3=4.1 num4=4.5
num3=4.5 num4=4.1
3
函数模版原理
函数模板是一个蓝图,它本身并不是函数,是编译器用使用方式产生特定具体类型函数的模具。所以其实模板就是将本来应该我们做的重复的事情交给了编译器
在函数调用的过程中,直接调试时不论是int
类型还是double
类型都会走到模版,但是进入反汇编可以看到当形参是int
类型时,编译器会进入int
类型的函数,同样double
类型类似
所以,函数模版是告诉编译器应该生成何种类型的函数,如下图所示
在编译器编译阶段,对于模板函数的使用,编译器需要根据传入的实参类型来推演生成对应类型的函数以供调用
函数模版实例化
用不同类型的参数使用函数模板时,称为函数模板的实例化。
模板参数实例化分为:隐式实例化和显式实例化
隐式实例化:让编译器根据实参类型自动推演出形式参数类型
#include <iostream>
using namespace std;template<class T>
void Swap(T& num1, T& num2)
{T tmp = num1;num1 = num2;num2 = tmp;
}int main()
{int num1 = 1, num2 = 2;printf("num1=%d num2=%d\n", num1, num2);Swap(num1, num2);//自动推演出int类型printf("num1=%d num2=%d\n", num1, num2);return 0;
}
输出结果:
num1=1 num2=2
num1=2 num2=1
但是,当模版参数类型种类个数与实参种类个数不匹配时,编译器将无法自动推演
#include <iostream>
using namespace std;template<class T>
void add(T& num1, T& num2)
{return num1 + num2;
}int main()
{int num1 = 1;double num2 = 2.0;add(num1, num2);//无法自动推演return 0;
}
报错信息:
没有与参数列表匹配的 函数模板 "Swap" 实例
在上面的代码中,函数模版中只有一种类型,但是实际调用函数传递的实际参数对应两种类型,此时因为类型不对应编译报错
第一种解决方式:添加额外种类的模版参数
#include <iostream>
using namespace std;template<class T, class R>
T add(T& num1, R& num2)
{return num1 + num2;
}int main()
{int num1 = 1;double num2 = 2.0;cout << add(num1, num2) << endl;//当函数模版有两种参数时可以自动推演return 0;
}
输出结果:
3
在上面的代码中,类型T
被推演为int
,类型R
被推演为double
,但是有个返回值问题,因为函数返回值只能为一种,所以存在精度丢失
第二种解决方式:对某一种类型进行强制转换
以强制转换int
类型为例
#include <iostream>
using namespace std;template<class T>
T add(T num1, T num2)
{return num1 + num2;
}int main()
{int num1 = 1;double num2 = 2.2;cout << add((double)num1, num2); << endl;//将int类型转换为double类型return 0;
}
输出结果:
3.2
第三种解决方式:显式实例化
#include <iostream>
using namespace std;template<class T>
T add(T num1, T num2)
{return num1 + num2;
}int main()
{int num1 = 1;double num2 = 2.2;cout << add<double>(num1, num2) << endl;//强制指定T为double类型此时会隐式转换return 0;
}
输出结果:
3.2
对于显式实例化来说,如果此时类型依旧不匹配,编译器会尝试进行隐式类型转换,如果无法转换成功编译器将会报错
📌
注意,第二种方式和第三种方式都有强制性,指定的类型时何种类型函数模版就一定是何种类型,哪怕函数模版的形参是同类型的引用编译器也无法识别
模版参数匹配规则
- 一个非模板函数可以和一个同名的函数模板同时存在,而且该函数模板还可以被实例化为这个非模板函数
- 对于非模板函数和同名函数模板,如果其他条件都相同,在调动时会优先调用非模板函数而不会从该模板产生出一个实例。如果模板可以产生一个具有更好匹配的函数, 那么将选择模板
#include <iostream>
using namespace std;
//同名函数模版和非模版函数
//函数模版
template<class T, class R>
R add(T num1, R num2)
{return num1 + num2;
}//单独处理整型加法
int add(int num1, int num2)
{return num1 + num2;
}int main()
{int num1 = 1;int num2 = 2;cout << add(num1, num2) << endl;//此时编译器会调用单独处理整型加法的函数,而不是根据函数模版推演出新的int形参函数double num3 = 2.2;cout << add(num1, num3) << endl;//编译器直接推演出不需要强制转换的函数return 0;
}
输出结果:
3
3.2
类模版
类模版基础语法
template<typename name1, typename name2>
class 类名
{//类体
};
在C++中,使用template
关键字创建模版,使用<>
包裹类体体中需要使用到的类型,typename name1
用于指代类型,在使用类时自动匹配数据类型,默认不会隐式类型转换,模版下方正常写类即可
📌
注意,使用模版类创建类对象时必须显式指定类型
#include <iostream>
using namespace std;template<class T>
class SeqList
{
private:T* _a;int _size;int _capacity = 4;
public:SeqList():_a(nullptr){_a = new T[_capacity];}~SeqList(){delete[] _a;_size = _capacity = 0;}
};int main()
{//类模版必须显式制定类型SeqList<int> s1;//存放int类型数据的顺序表SeqList<double> s2;//存放double类型的顺序表return 0;
}
📌
类模板实例化与函数模板实例化不同,类模板实例化需要在类模板名字后跟<>
,然后将实例化的类型放在<>
中即可,类模板名字不是真正的类,而实例化的结果才是真正的类
例如上面的代码中有两个类,一个是SeqList<int>
,一个是SeqList<double>
如果声明和定义分开时,域作用限定符左侧的域名一定要带上模版参数列表
SeqList<int>::~SeqList()
{delete[] _a;_size = _capacity = 0;
}SeqList<double>::~SeqList()
{delete[] _a;_size = _capacity = 0;
}
相关文章:

C++模版(基础)
目录 C泛型编程思想 C模版 模版介绍 模版使用 函数模版 函数模版基础语法 函数模版原理 函数模版实例化 模版参数匹配规则 类模版 类模版基础语法 C泛型编程思想 泛型编程:编写与类型无关的通用代码,是代码复用的一种手段。 模板是泛型编程…...

MySQL驱动Add Batch优化实现
MySQL 驱动 Add Batch 优化实现 MySQL 驱动会在 JDBC URL 添加 rewriteBatchedStatements 参数时,对 batch 操作进行优化。本文测试各种参数组合的行为,并结合驱动代码简单分析。 batch参数组合行为 useServerPrepStmts 参数 PreparedStatement psmt…...

手撕算法-数组中的第K个最大元素
描述 分析 使用小根堆,堆元素控制在k个,遍历数组构建堆,最后堆顶就是第K个最大的元素。 代码 class Solution {public int findKthLargest(int[] nums, int k) {// 小根堆PriorityQueue<Integer> queue new PriorityQueue<>…...

【vue】computed和watch的区别和应用场景
Computed 和 Watch 是 Vue.js 中用于监视数据变化的两个不同特性,它们各自有不同的应用场景和功能。 Computed: 计算属性(Computed properties)用于声明基于其他数据属性的计算值。它具有缓存功能,只有在依赖的数…...

ARM.day8
1.自己设置温度湿度阈值,当温度过高时,打开风扇,蜂鸣器报警 2.当湿度比较高时,打开LED1灯,蜂鸣器报警 main.c #include "si7006.h" #include "CH1.h" #include "led.h" // 延时函数in…...

SpringCloud Gateway工作流程
Spring Cloud Gateway的工作流程 具体的流程: 用户发送请求到网关 请求断言,用户请求到达网关后,由Gateway Handler Mapping(网关处理器映射)进行Predicates(断言),看一下哪一个符合…...

西井科技与安通控股签署战略合作协议 共创大物流全新生态
2024年3月21日,西井科技与安通控股在“上海硅巷”新象限空间正式签署战略合作框架协议。双方基于此前在集装箱物流的成功实践与资源优势,积极拓展在AI数字化产品、新能源自动驾驶解决方案和多场景应用,以及绿色物流链等领域的深度探索、强强联…...

CCCorelib 点云RANSAC拟合球体(CloudCompare内置算法库)
文章目录 一、简介二、实现代码三、实现效果参考资料一、简介 RANSAC是一种随机参数估计算法。RANSAC从样本中随机抽选出一个样本子集,使用最小方差估计算法对这个子集计算模型参数,然后计算所有样本与该模型的偏差,再使用一个预先设定好的阈值与偏差比较,当偏差小于阈值时…...

map china not exists. the geojson of the map must be provided.
map china not exists. the geojson of the map must be provided. 场景:引入echarts地图报错map china not exists. the geojson of the map must be provided. 原因: echarts版本过高,ECharts 之前提供下载的矢量地图数据来自第三方&…...

Redis如何删除大key
参考阿里云Redis规范 查找大key: redis-cli --bigkeys 1、String类型: Redis 4.0及以后版本提供了UNLINK命令,该命令与DEL命令类似,但它会在后台异步删除key,不会阻塞当前客户端,也不会阻塞Redis服务器的…...

JRT菜单
上一章搭建了登录界面的雏形和抽取了登录接口。给多组使用登录和菜单功能提供预留,做到不强行入侵别人业务。任何产品只需要按自己表实现登录接口后配置到容器即可共用登录界面和菜单部分。最后自己的用户关联到JRT角色表即可。 登录效果 这次构建菜单体系 首先用…...

《海王2》观后感
前言 我原本计划电影上映之后,去电影院观看的,但时间过得飞快,一眨眼这都快4月份了,查了一下,电影院早就没有排片了,所以只能在B站看了,这里不得不吐槽一下,原来花了4块钱购买观看还…...

[蓝桥杯 2023 省 A] 颜色平衡树:从零开始理解树上莫队 一颗颜色平衡树引发的惨案
十四是一名生物工程的学生,他已经7年没碰过信息学竞赛了,有一天他走在蓝桥上看见了一颗漂亮的颜色平衡树: [蓝桥杯 2023 省 A] 颜色平衡树 - 洛谷 十四想用暴力解决问题,他想枚举每个节点,每个节点代表…...

maya打开bvh脚本
目录 maya打开脚本编辑器 运行打开bvh脚本 maya导出bvh脚本 maya打开脚本编辑器 打开Maya软件,点击右下角 “脚本编辑器” 运行打开bvh脚本 https://github.com/jhoolmans/mayaImporterBVH/blob/master/bvh_importer.py import os import re from typing impo…...

【JavaSE】数据类型和运算符
前言 从这一篇我们开始Java的学习~ 欢迎关注个人主页:逸狼 创造不易,可以点点赞吗~ 如有错误,欢迎指出~ 目录 前言 Java第一个程序 字面常量 字面常量的分类 结合代码理解 类型转换 类型提升 byte与byte的运算 正确写法 字符串类型St…...

Docker 哲学 - ip 的组成规则 与 网关介绍
在 IP 地址中,我们通常将 IP 地址分为两部分:网络部分和主机部分。网络部分用于标识网络,主机部分用于标识该网络中的特定主机。 IP 地址的每个部分(也被称为一个八位组或一个字节)可以是从0到255的任何值。 一个 IPv4…...

数学建模竞赛真的是模型解题一般,但是论文出彩而获奖的吗?
最近,数乐君发现有同学会有这样的问题:在数学建模国赛中,会因为参赛团队的模型解题一般,但论文写得非常精彩而获奖吗? 是的,确实会存在这样的情况。 我们都知道数学建模竞赛最终都是以提交成品论文的形式…...

深度学习常见的三种模型
深度学习模型实际上是一个包含多个隐藏层的神经网络,目前主要有卷积神经网络(CNN)、深度置信网络(DBN)、循环神经网络(RNN)。 1) 卷积神经网络 在机器学习领域,卷积神经网络属于前…...

接口自动化测试分层设计与实践总结
🍅 视频学习:文末有免费的配套视频可观看 🍅 关注公众号:互联网杂货铺,回复1 ,免费获取软件测试全套资料,资料在手,涨薪更快 接口测试三要素: 参数构造 发起请求&#x…...

集合(下)Map集合的使用
文章目录 前言一、Map接口二、Map接口的实现类 1.HashMap类2.TreeMap类总结 前言 Map集合没有继承Collection接口,不能像List集合和Set集合那样直接使用Collection接口的方法。Map集合其自身通过以key到value的映射关系实现的集合,也有相应的许多方法。类…...

AAPT: error: resource android:attr/dialogCornerRadius not found.
ERROR:D:\android.gradle\caches\transforms-3\b3b98118f65da38d0ad9da84cfc70a72\transformed\appcompat-1.0.0\res\values-v28\values-v28.xml:5:5-8:13: AAPT: error: resource android:attr/dialogCornerRadius not found. 请帮我看看这个错误是什么意思。我改如何做。 这个…...

数字功放VS模拟功放,选择适合你的音频解决方案
数字功放和模拟功放是音频系统中常用的两种功放技术,适用于不同的音频应用,都具有各自的优势和特点。本文将为您详细介绍数字功放和模拟功放的差异,并帮助您找到适合自己的音频解决方案。 1、数字功放是一种利用数字信号处理技术的功放。它将…...

5.88 BCC工具之tcpsynbl.py解读
一,工具简介 tcpsynbl工具以直方图的形式显示SYN到达时的TCP SYN积压大小。这可以让我们了解应用程序距离达到积压限制并丢弃SYN(导致SYN重传产生性能问题)还有多远。 TCP SYN 数据包则通常用于启动 TCP 三次握手过程的第一次握手。 二,代码示例 #!/usr/bin/env python…...

GVRP实现vlan的自动创建和注册
拓扑图 资源已上传 流程 第一、每台交换机的全局及端口下使能GVRP功能 第二、配置交换机的二层连通性,交换机某些端口配置Trunk端口,并允许相应的vlan通过 第三、在交换机4和5配置静态vlan100,然后查看1和3交换机是否有vlan100的定义&…...

Oracle VM VirtualBox修改磁盘大小
一、 修改虚拟机磁盘大小 先把虚拟机停掉。再增加磁盘大小。 路径中有空格的用""包起来。 D:\Program Files\Oracle\VirtualBox>VBoxManage.exe modifyhd "D:\Program Files\VirtualBox VMs\mycentos\mycentos.vdi" --resize 30000 0%...10%...20%...3…...

【嵌入式硬件】步进电机
1.步进电机简介 1.1步进电机基本原理 步进电机的英文是stepping motor。step的中文意思是行走、迈步。所以仅从字面上我们就可以得知,步进电机就是一步一步移动的电动机。说的官方一点儿,步进电机是一种将电脉冲信号转换成相应角位移或者线位移的电动机(直线电机)。下图为…...

FlyControls 是 THREE.js 中用于实现飞行控制的类,它用于控制摄像机在三维空间中的飞行。
demo演示地址 FlyControls 是 THREE.js 中用于实现飞行控制的类,它用于控制摄像机在三维空间中的飞行。 入参: object:摄像机对象,即要控制的摄像机。domElement:用于接收用户输入事件的 HTML 元素,通常…...

【Java程序设计】【C00366】基于(JavaWeb)Springboot的纹理生产图片系统(有论文)
TOC 博主介绍:java高级开发,从事互联网行业六年,已经做了六年的毕业设计程序开发,开发过上千套毕业设计程序,博客中有上百套程序可供参考,欢迎共同交流学习。 项目简介 项目获取 🍅文末点击卡片…...

编译原理Lab. 1 初代编译器实验说明和要求
目录 Lab. 1 初代编译器实验说明和要求一、初代编译器功能描述二、初代编译器文法要求三、初代编译器测试样例四、初代编译器提交要求五、初代编译器实验测试框架说明 代码与思路 Lab. 1 初代编译器实验说明和要求 一、初代编译器功能描述 初代编译器将 C 语言顺序语句序列翻…...

python判断工作日,节假日
一、概述 需要判断一个日期是否为工作日,节假日。 找到一个现成的插件,蛮好用的。 插件介绍 https://pypi.org/project/chinesecalendar/ 判断某年某月某一天是不是工作日/节假日。 支持 2004年 至 2020年,包括 2020年 的春节延长。 兼容…...