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的映射关系实现的集合,也有相应的许多方法。类…...
SpringBoot-17-MyBatis动态SQL标签之常用标签
文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…...
Lombok 的 @Data 注解失效,未生成 getter/setter 方法引发的HTTP 406 错误
HTTP 状态码 406 (Not Acceptable) 和 500 (Internal Server Error) 是两类完全不同的错误,它们的含义、原因和解决方法都有显著区别。以下是详细对比: 1. HTTP 406 (Not Acceptable) 含义: 客户端请求的内容类型与服务器支持的内容类型不匹…...
基于Flask实现的医疗保险欺诈识别监测模型
基于Flask实现的医疗保险欺诈识别监测模型 项目截图 项目简介 社会医疗保险是国家通过立法形式强制实施,由雇主和个人按一定比例缴纳保险费,建立社会医疗保险基金,支付雇员医疗费用的一种医疗保险制度, 它是促进社会文明和进步的…...
iPhone密码忘记了办?iPhoneUnlocker,iPhone解锁工具Aiseesoft iPhone Unlocker 高级注册版分享
平时用 iPhone 的时候,难免会碰到解锁的麻烦事。比如密码忘了、人脸识别 / 指纹识别突然不灵,或者买了二手 iPhone 却被原来的 iCloud 账号锁住,这时候就需要靠谱的解锁工具来帮忙了。Aiseesoft iPhone Unlocker 就是专门解决这些问题的软件&…...
【SQL学习笔记1】增删改查+多表连接全解析(内附SQL免费在线练习工具)
可以使用Sqliteviz这个网站免费编写sql语句,它能够让用户直接在浏览器内练习SQL的语法,不需要安装任何软件。 链接如下: sqliteviz 注意: 在转写SQL语法时,关键字之间有一个特定的顺序,这个顺序会影响到…...
SpringCloudGateway 自定义局部过滤器
场景: 将所有请求转化为同一路径请求(方便穿网配置)在请求头内标识原来路径,然后在将请求分发给不同服务 AllToOneGatewayFilterFactory import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; impor…...
SAP学习笔记 - 开发26 - 前端Fiori开发 OData V2 和 V4 的差异 (Deepseek整理)
上一章用到了V2 的概念,其实 Fiori当中还有 V4,咱们这一章来总结一下 V2 和 V4。 SAP学习笔记 - 开发25 - 前端Fiori开发 Remote OData Service(使用远端Odata服务),代理中间件(ui5-middleware-simpleproxy)-CSDN博客…...
中医有效性探讨
文章目录 西医是如何发展到以生物化学为药理基础的现代医学?传统医学奠基期(远古 - 17 世纪)近代医学转型期(17 世纪 - 19 世纪末)现代医学成熟期(20世纪至今) 中医的源远流长和一脉相承远古至…...
uniapp 开发ios, xcode 提交app store connect 和 testflight内测
uniapp 中配置 配置manifest 文档:manifest.json 应用配置 | uni-app官网 hbuilderx中本地打包 下载IOS最新SDK 开发环境 | uni小程序SDK hbulderx 版本号:4.66 对应的sdk版本 4.66 两者必须一致 本地打包的资源导入到SDK 导入资源 | uni小程序SDK …...
【Android】Android 开发 ADB 常用指令
查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...
