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

如何编写一个通用的函数?

在这里插入图片描述

🎈个人主页:🎈 :✨✨✨初阶牛✨✨✨
🐻推荐专栏1: 🍔🍟🌯C语言初阶
🐻推荐专栏2: 🍔🍟🌯C语言进阶
🔑个人信条: 🌵知行合一
金句分享:
✨你要狠下心来去努力,努力变成一个很厉害的人.✨

前言

本文主要讲解如何使用简单的模板,了解模板的原理以及基本知识.

目录

  • 前言
  • 一、函数模板
    • (1)函数模板的格式
    • (2)函数模板的原理(重点)
    • (3)模板参数的显示实例化
    • (4)模板匹配
  • 二、类模板

一、函数模板

模板的作用:

C++模板的作用是支持泛型编程。==泛型编程=是一种编程范式,它只考虑算法或数据结构的抽象,而不考虑具体的数据类型。通过使用模板,可以编写一种通用的算法或数据结构,而不需要为每种数据类型都编写一遍相关代码。模板可以用于函数、类、结构体等地方,以实现通用的算法和数据结构。使用模板可以提高代码的复用性和可读性,减少代码的重复编写。

示例:实现一个交换函数.

使用模板之前:

void swap(int& a, int& b)
{int tmp = a;a = b;b = tmp;
}
void swap(double& a, double& b)
{double tmp = a;a = b;b = tmp;
}void swap(char& a, char& b)
{char tmp = a;a = b;b = tmp;
}void test1()
{//交换整形int a = 2, b = 3;cout << "a=" << a << "  " << "b=" << b << endl;swap(a, b);cout << "a=" << a << "  " << "b=" << b << endl;//交换字符型char c1 = 'd', c2 = 'f';cout << "c1=" << c1 << "  " << "c2=" << c2 << endl;swap(c1, c2);cout << "c1=" << c1 << "  " << "c2=" << c2 << endl;//交换...
}

上述实现过程中使用函数重载实现.但是函数重载会有一些不合适的问题.

  1. 函数重载只是重载的函数类型不同,代码复用率比较低,对于一个新的类型又要增加新的函数.
  2. 由于功能基本一样,只是类型不同,导致代码的可维护性比较,一个出错可能所有的重载均出错,均要修改.

这时,函数模板就派上用场了.

(1)函数模板的格式

template<typename T1, typename T2,......,typename Tn>
返回值类型 + 函数名 +(参数列表){}
其中,typename 可以使用class代替,不能使用struct代替.

示例:使用模板后的通用交换函数.

template <class T>//模板
void swap(T& a, T& b)//T会根据传参的对象进行推导为相应的类型
{T tmp = a;a = b;b = tmp;
}
void test1()
{//交换整形int a = 2, b = 3;cout << "a=" << a << "  " << "b=" << b << endl;swap(a, b);cout << "a=" << a << "  " << "b=" << b << endl;//交换字符型char c1 = 'd', c2 = 'f';cout << "c1=" << c1 << "  " << "c2=" << c2 << endl;swap(c1, c2);cout << "c1=" << c1 << "  " << "c2=" << c2 << endl;//交换...
}

(2)函数模板的原理(重点)

函数模板类似于一个模具,它本身并不是函数,是编译器用使用方式产生特定具体类型函数的模具。所以其实模板就是将本来应该我们做的重复的事情交给了编译器去做.

这就类似于古代的印刷术,如果每本书都需要手写,那效率是否太低了,还有各种情况可能会出错.但是印刷术的使用,就可以使用模具生成.
在这里插入图片描述

函数模板的原理是通过将类型参数化,使函数能够在编译时根据实际参数的类型推断生成具体的函数实例。编译器会根据调用函数时的参数类型,实例化出适合该类型的函数版本。

比如:
当用double类型使用函数模板时,编译器通过对实参类型的推演,将T确定为double类型,然后产生一份专门处理double类型的代码.当用int类型使用函数模板时,编译器通过对实参类型的推演,将T确定为int类型,然后产生一份专门处理int类型的代码.如上图所示.

(3)模板参数的显示实例化

上面我们实现的交换函数,模板根据传参时不同的参数,自动推演出函数参数的实际类型.我们称这类通过编译器进行自动推导的实例化模板参数称为模板参数的隐式实例化.

那什么是显示实例化呢?

template <typename T>
T add(const T& a, const T& b)
{return a + b;
}
void test1()
{int a = 2, b = 3;double d1 = 2.5, d2 = 4.1;cout << add(a, b) << endl;cout << add(d1, d2) << endl;//下面这句会报错,因为一个模板参数无法在一个函数中实例化为2个不同类型的参数,一个int,一个double//cout << add(a, d2) << endl;
}

一个函数模板参数在同一个函数中,无法被识别为不同的两个实例类型参数,当编译器推导出aint时,又推出d2double类型,则编译器陷入两难.

就好比:
int:妈妈说今天不许出去玩!
double:爸爸说今天可以出去玩!
编译器:我听谁的.

解决方案:

直接将参数先强转为一样的,当模板函数接收到参数时,就只有一样的结果了.

	//解决方法1:传参时将其中不同的参数强转,使参数们相同cout << add(a, (int)d2) << endl;cout << add((double)a, d2) << endl;

模板参数的显示实例化:

让爸妈先商量好听谁的.

	//解决方法2:显示指定模板的参数cout << add<int>(a, d2) << endl;	//听妈妈的cout << add<double>(a, d2) << endl;	//听爸爸的

我们应当是考虑如何在调用时采取不同的调用方式去满足我们的需求,千万不要想着去修改模板函数的返回值,参数使他们固定生成,那模板就不通用了,而且不是什么时候我们都可以去修改模板的.
错误示例:

template <typename T>
T add(const T& a, const int& b)//直接修改参数,进行固定
{return a + b;
}

(4)模板匹配

对于函数名相同的非模板函数模板函数同时存在时,编译器会优先选择非模板函数.除非模板可以产生更好的匹配函数,才会选择模板.

编译器:有现成的为啥不用.

void swap(double& a, double& b)
{double tmp = a;a = b;b = tmp;
}
//template <class  T>
template <typename  T>//函数模板
void swap(T& a, T& b)
{T tmp = a;a = b;b = tmp;
}
void test1()
{//交换doubledouble d1 = 2.5, d2 = 4.5;//非模板函数和模板函数同时存在时,编译器优先选择非模板函数,有现成的为啥不用?非得自己去再模具刻一个?swap(d1, d2);cout << "d1=" << d1 << "  " << "d2=" << d2 << endl;//交换整形int a = 2, b = 3;//没有现成的非模板函数,则编译器调用模板函数去实例化一份swap(a, b);cout << "a=" << a << "  " << "b=" << b << endl;
}

交换double型数据时,会调用void swap(double& a, double& b)函数,因为有现成的可以调用.
交换int整形时,则会调用模板函数void swap(T& a, T& b),实例化生成int型的函数.

在这里插入图片描述
小知识:
模板函数不允许自动类型转换,但普通函数可以进行自动类型转换.
因为模板函数的参数是通过参数类型进行推导的.

二、类模板

类模板的格式

template <typename T>
class A
{//成员
}

类模板在后续学习STL时候会具体介绍,目前了解一下即可,使用方法与函数模板类似,这里就不过多介绍了.

template <typename T>
class A
{
public:A(size_t capacity = 10): _data(new T[capacity]), _size(0), _capacity(capacity){}void push_back() {//...}~A(){delete _data;_size = 0;_capacity = 0;}private:T* _data;size_t _size;size_t _capacity;
};void test3()
{A<int> a1;			//实例化为存储int数据的类A<double> a2;		//实例化为存储double数据的类
}

本文只是对模板的初步了解,后续会遇到更加复杂的模板,比如多参数的模板等,知识一点点的学,不求速成,坚持一点点的积累,一起加油吧!
今天就讲到这里了,拜拜.

在这里插入图片描述

相关文章:

如何编写一个通用的函数?

&#x1f388;个人主页:&#x1f388; :✨✨✨初阶牛✨✨✨ &#x1f43b;推荐专栏1: &#x1f354;&#x1f35f;&#x1f32f;C语言初阶 &#x1f43b;推荐专栏2: &#x1f354;&#x1f35f;&#x1f32f;C语言进阶 &#x1f511;个人信条: &#x1f335;知行合一 金句分享:…...

uni-app封装api请求

前端封装api请求 前端封装 API 请求可以提高代码的可维护性和重用性&#xff0c;同时使得 API 调用更加简洁和易用。 下面是一种常见的前端封装 API 请求的方式&#xff1a; 创建一个 API 封装模块或类&#xff1a;可以使用 JavaScript 或 TypeScript 创建一个独立的模块或类来…...

深度学习从入门到实际项目资料汇总

图片来源于AiLake&#xff0c;如若侵权&#xff0c;请联系博主删除 文章目录 1. 介绍2. 深度学习相关学习资料2.1 [《动手学深度学习》](http://zh.d2l.ai/index.html)2.2 [导航文库](https://docs.apachecn.org/#1be32667e7914f03afb3c39239bd2525)2.3 [AI学习地图&#xff0c…...

单元测试到底是什么?应该怎么做?

一、什么是单元测试&#xff1f; 单元测试&#xff08;unit testing&#xff09;&#xff0c;是指对软件中的最小可测试单元进行检查和验证。至于“单元”的大小或范围&#xff0c;并没有一个明确的标准&#xff0c;“单元”可以是一个函数、方法、类、功能模块或者子系统。 …...

JavaWeb-Listener监听器

目录 监听器Listener 1.功能 2.监听器分类 3.监听器的配置 4.ServletContext监听 5.HttpSession监听 6.ServletRequest监听 监听器Listener 1.功能 用于监听域对象ServletContext、HttpSession和ServletRequest的创建&#xff0c;与销毁事件监听一个对象的事件&#x…...

js数组常用的方法(总结)

目录 1.数组头和尾操作——push、pop、unshift/shift 2、数组转为字符串 —— join() 3、数组截取 —— slice() 4、数组更新 —— splice() 5、反转数组 —— reverse() 6、连接数组 —— concat() 7、ES6连接数组 —— ... ES5数组新增方法 8、索引方法 —— indexO…...

Linux:shell脚本:基础使用(5)《正则表达式-sed工具》

sed是一种流编辑器&#xff0c;它是文本处理中非常中的工具&#xff0c;能够完美的配合正则表达式使用&#xff0c;功能不同凡响。 处理时&#xff0c;把当前处理的行存储在临时缓冲区中&#xff0c;称为“模式空间”&#xff08;pattern space&#xff09;&#xff0c;接着用s…...

关于ubuntu下面安装cuda不对应版本的pyTorch

最近换了台新的linux的ubuntu的服务器&#xff0c;发现其实际安装的cuda版本为11.4&#xff0c;但是pytorch官方给出的针对cuda 11.4并没有具体的pytorch的安装指令&#xff0c;于是采用不指定pytorch版本直接安装让其自动搜索得到即可 直接通过&#xff1a; pip3 install tor…...

【SA8295P 源码分析】26 - QNX Ethernet MAC 驱动 之 emac_rx_thread_handler 数据接收线程 源码分析

【SA8295P 源码分析】26 - QNX Ethernet MAC 驱动 之 emac_rx_thread_handler 数据接收线程 源码分析 一、emac_rx_thread_handler():通过POLL 轮询方式获取数据二、emac_rx_poll_mq():调用 pdata->clean_rx() 来处理消息三、emac_configure_rx_fun_ptr():配置 pdata->…...

70 # 协商缓存的配置:通过修改时间

对比&#xff08;协商&#xff09;缓存 比较一下再去决定是用缓存还是重新获取数据&#xff0c;这样会减少网络请求&#xff0c;提高性能。 对比缓存的工作原理 客户端第一次请求服务器的时候&#xff0c;服务器会把数据进行缓存&#xff0c;同时会生成一个缓存标识符&#…...

适合程序员的DB性能测试工具 JMeter

背景 1、想要一款既要能压数到mysql&#xff0c;又要能压数到postGre&#xff0c;还要能压数到oracle的自动化工具 2、能够很容易编写insert sql&#xff08;因为需要指定表和指定字段类型压数据&#xff09;&#xff0c;然后点击运行按钮后&#xff0c;就能直接运行&#xff…...

java实现人物关系抽取

java实现人物关系抽取 人物关系抽取是实体关系抽取的一种情况。实际上是两个过程&#xff1a;命名实体识别和关系抽取。 Java人物关系抽取是指从文本中提取出与Java相关的人物之间的关系。这个过程可以通过自然语言处理和文本分析的方法来实现。具体的步骤包括&#xff1a; 文本…...

Docker网络与资源控制

一、Docker 网络实现原理 Docker使用Linux桥接&#xff0c;在宿主机虚拟一个Docker容器网桥(docker0)&#xff0c;Docker启动一个容器时会根据Docker网桥的网段分配给容器一个IP地址&#xff0c;称为Container-IP&#xff0c;同时Docker网桥是每个容器的默认网关。因为在同一宿…...

图片怎么转换成pdf格式?可以试试这样转换

图片怎么转换成pdf格式&#xff1f;图片转换成PDF格式是一个常见的需求&#xff0c;无论是为了方便存储还是为了分享文件&#xff0c;将图片转换成PDF格式都是一个不错的选择。有许多软件和在线工具可以帮助你完成这个任务&#xff0c;下面就给大家介绍一款转换工具。 【迅捷PD…...

[国产MCU]-W801开发实例-GPIO输入与中断

GPIO输入与中断 文章目录 GPIO输入与中断1、硬件准备2、软件准备3、驱动实现4、驱动测试W801的GPIO支持软件配置中断,中断触发方式包含:上升沿触发、下降沿触发、高电平触发、低电平触发。本文在前面[ 国产MCU]-W801开发实例-按键与GPIO输入的基础上实现GPIO中断配置。 1、硬…...

Layui列表表头去掉复选框改为选择

效果&#xff1a; 代码&#xff1a; // 表头复选框去掉改为选择 $(".layui-table th[data-field"0"] .layui-table-cell").html("<span>选择</span>");...

Flutter实战·第二版-第三章 基础组件笔记

第三章&#xff1a;基础组件 3.1文本及样式 3.1.1 Text Text("Hello world",textAlign: TextAlign.left, );Text("Hello world! Im Jack. "*4,maxLines: 1,overflow: TextOverflow.ellipsis, );Text("Hello world",textScaleFactor: 1.5, );3.1…...

一文彻底理解时间复杂度和空间复杂度(附实例)

目录 1 PNP&#xff1f;2 时间复杂度2.1 常数阶复杂度2.2 对数阶复杂度2.3 线性阶复杂度2.4 平方阶复杂度2.5 指数阶复杂度2.6 总结 3 空间复杂度 1 PNP&#xff1f; P类问题(Polynomial)指在多项式时间内能求解的问题&#xff1b;NP类问题(Non-Deterministic Polynomial)指在…...

Mysql的索引详解

零. 索引类型概述 1. 实际开发中使用的索引种类 主键索引唯一索引普通索引联合索引全文索引空间索引 2. 索引的格式类型 BTree类型Hash类型FullText类型&#xff08;全文索引)RTree类型&#xff08;空间索引) MySQL 的索引方法&#xff0c;主要包括 BTREE 和 HASH。 顾名思…...

.netcore windows app启动webserver

创建controller: using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Text.Json.Serialization; using System.Threading.Tasks;namespace MyWorker.…...

分布式增量爬虫实现方案

之前我们在讨论的是分布式爬虫如何实现增量爬取。增量爬虫的目标是只爬取新产生或发生变化的页面&#xff0c;避免重复抓取&#xff0c;以节省资源和时间。 在分布式环境下&#xff0c;增量爬虫的实现需要考虑多个爬虫节点之间的协调和去重。 另一种思路&#xff1a;将增量判…...

Rust 开发环境搭建

环境搭建 1、开发工具RustRover 或者vs code 2、Cygwin64 安装 https://cygwin.com/install.html 在工具终端执行&#xff1a; rustup toolchain install stable-x86_64-pc-windows-gnu rustup default stable-x86_64-pc-windows-gnu ​ 2、Hello World fn main() { println…...

vue3 daterange正则踩坑

<el-form-item label"空置时间" prop"vacantTime"> <el-date-picker v-model"form.vacantTime" type"daterange" start-placeholder"开始日期" end-placeholder"结束日期" clearable :editable"fal…...

【UE5 C++】通过文件对话框获取选择文件的路径

目录 效果 步骤 源码 效果 步骤 1. 在“xxx.Build.cs”中添加需要使用的模块 &#xff0c;这里主要使用“DesktopPlatform”模块 2. 添加后闭UE编辑器&#xff0c;右键点击 .uproject 文件&#xff0c;选择 "Generate Visual Studio project files"&#xff0c;重…...

ArcPy扩展模块的使用(3)

管理工程项目 arcpy.mp模块允许用户管理布局、地图、报表、文件夹连接、视图等工程项目。例如&#xff0c;可以更新、修复或替换图层数据源&#xff0c;修改图层的符号系统&#xff0c;甚至自动在线执行共享要托管在组织中的工程项。 以下代码展示了如何更新图层的数据源&…...

SQL注入篇-sqlmap的配置和使用

在之前的皮卡丘靶场第五期SQL注入的内容中我们谈到了sqlmap&#xff0c;但是由于很多朋友看不了解命令行格式&#xff0c;所以是纯手动获取数据库信息的 接下来我们就用sqlmap来进行皮卡丘靶场的sql注入学习&#xff0c;链接&#xff1a;https://wwhc.lanzoue.com/ifJY32ybh6vc…...

Python的__call__ 方法

在 Python 中&#xff0c;__call__ 是一个特殊的魔术方法&#xff08;magic method&#xff09;&#xff0c;它允许一个类的实例像函数一样被调用。当你在一个对象后面加上 () 并执行时&#xff08;例如 obj()&#xff09;&#xff0c;Python 会自动调用该对象的 __call__ 方法…...

Springboot多数据源配置实践

Springboot多数据源配置实践 基本配置文件数据库配置Mapper包Model包Service包中业务代码Mapper XML文件在某些复杂的业务场景中,我们可能需要使用多个数据库来存储和管理不同类型的数据,而不是仅仅依赖于单一数据库。本技术文档将详细介绍如何在 Spring Boot 项目中进行多数…...

【SSM】SpringMVC学习笔记7:前后端数据传输协议和异常处理

这篇学习笔记是Spring系列笔记的第7篇&#xff0c;该笔记是笔者在学习黑马程序员SSM框架教程课程期间的笔记&#xff0c;供自己和他人参考。 Spring学习笔记目录 笔记1&#xff1a;【SSM】Spring基础&#xff1a; IoC配置学习笔记-CSDN博客 对应黑马课程P1~P20的内容。 笔记2…...

PCA笔记

✅ 问题本质&#xff1a;为什么让矩阵 TT 的行列式为 1&#xff1f; 这个问题通常出现在我们对数据做**线性变换&#xff08;旋转/缩放&#xff09;**的时候&#xff0c;比如在 PCA 中把数据从原始坐标系变换到主成分方向时。 &#x1f4cc; 回顾一下背景 在 PCA 中&#xff…...