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

news2024/11/18 10:57:51

在这里插入图片描述

🎈个人主页:🎈 :✨✨✨初阶牛✨✨✨
🐻推荐专栏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()
{
	//交换double
	double 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数据的类
}

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

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/905881.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

微信个人号开发,实现机器人辅助社群操作

微信 iPad 协议是指用于在 iPad 设备上使用微信应用的技术协议。一般来说&#xff0c;通过该协议可以将微信账号同步到 iPad 设备上&#xff0c;并且可以在 iPad 上发送和接收微信消息&#xff0c;查看好友列表、聊天记录等功能。微信 iPad 协议是通过私有API实现的。 需要一定…

图片转文字生成器分享,再也不用担心转换麻烦

在现今社交媒体时代&#xff0c;图片已经成为人们传递信息的重要方式之一。但是&#xff0c;由于有些图片可能不易于理解或者文字更能够准确表达内容&#xff0c;将图片转换为文字已经成为了一种常见的需求。在本文中&#xff0c;我们将分享一些将图片转换为文字的方法以及需要…

SIP桌面式对讲主机 井下通信广播sip寻呼话筒

SV-8003VP是我司的一款SIP桌面式对讲主机&#xff0c;具有10/100M以太网接口&#xff0c;配置了麦克风输入和扬声器输出&#xff0c;还配置多达22个按键和2.8英寸液晶显示屏&#xff0c;可以配合SIP服务器使用。SV-8003VP网路寻呼话筒可以通过麦克风或者本地线路输入对SIP终端进…

Java-异常分析和处理

Java异常处理 在Java中&#xff0c;将程序执行过程中发生的不正常行为称为异常。比如之前写代码时经常遇到的&#xff1a; 算术异常 数组越界异常 空指针异常 异常的体系结构 Throwable&#xff1a;是异常体系的顶层类&#xff0c;其派生出两个重要的子类, Error 和 Except…

AgentBench:AI智能体对AI行业发展的意义

自从今年年初ChatGPT引爆了生成式AI的概念之后,就有大量的企业和投资人宣布要进入AI行业,一场轰轰烈烈的“百模大战”由此展开。然而,在短短半年之后,当初高调下场的大多数企业再也没有了相关声音。 如果我们对这场“百模大战”进行复盘,就会发现很多企业的失败其实从一…

C++ 11 实现简单线程池

线程池是一种多线程处理形式&#xff0c;处理过程中将任务添加到队列&#xff0c;然后在创建线程后自动启动这些任务。线程池线程都是后台线程。每个线程都使用默认的堆栈大小&#xff0c;以默认的优先级运行&#xff0c;并处于多线程单元中。如果某个线程在托管代码中空闲&…

新版QQ NT 桌面版如何实现内存优化

一、背景 QQ 作为国民级应用,从互联网兴起就一直陪伴着大家,是很多用户刚接触互联网就开始使用的应用。而 QQ 桌面版最近一次技术架构升级还是在移动互联网兴起之前,在多年迭代过程中,QQ 桌面版也积累了不少技术债务,随着业务的发展和技术的进步,当前的架构已经无法很好…

[git]gitpush提示remote: Permission to xxx.git denied to xxx

错误原因&#xff1a;git客户端你先前登录过其他用户导致&#xff0c;你用另一个账号push的时候用的先前用户 解决方法&#xff1a;删除先前用户用你想push用户重新登录 解决步骤&#xff1a; 打开控制面板-->查看方式选择大图标-->然后打开凭据管理器 找到github相关…

Mybatis的环境搭建

目录 一.Mybatis的环境搭建 1.创建项目 2.进行相关配置 3.安装插件 4.插件的使用 一.Mybatis的环境搭建 1.创建项目 1.1 创建Maven项目&#xff0c;配置好相应的JDK和archetype 1.2 给项目命名和创建目录结构 1.3 添加自定义Property自定义属性 2.进行相关配置 2.1 导入p…

Java代码审计12之反序列化漏洞的审计与利用

文章目录 1、重点函数&#xff0c;2、漏洞源码3、利用工具 ysoserial4、修复漏洞 1、重点函数&#xff0c; ObjectInputStream.readObjectObjectInputStream.readUnsharedXMLDecoder.readObjectYaml.loadXStream.fromXMLObjectMapper.readValueJSON.parseObject2、漏洞源码 接…

Golang GORM 单表删除

删除只有一个操作&#xff0c;delete。也是先找到再去删除。 可以删除单条记录&#xff0c;也可以删除多条记录。 var s Studentdb.Debug().Delete(&s, "age ?", 100)fmt.Println(s)[15.878ms] [rows:1] DELETE FROM student WHERE age 100var s Studentdb.De…

【AI生成】职场中每个人都应该具备识别、应对阴险领导的能力——微软晓晓朗读...

关于主题“职场中每个人都应该具备识别、应对阴险领导的能力”各种AI一共生成了8篇文章&#xff0c;如下&#xff1a; Claude(文章直白&#xff0c;简洁&#xff0c;全面) WebCopilot (创造力模式) 讯飞星火 谷歌Bard&#xff08;第一篇&#xff09; WebCopilot&#xff08;精确…

Javaweb基础学习(3)

Javaweb基础学习 web核心介绍一、HTTP1.1 HTTP介绍1.2、HTTP请求数据格式1.3、HTTP响应数据格式 二、Tomcat2.1 简介2.2 基本使用2.3 Tomcat配置2.4 Tomcat部署项目2.5 Web项目结构2.6 创建Maven Web项目 三、Servlet3.1、Servlet简介&快速入门3.2 创建Servlet步骤3.3 Serv…

怎么借助ChatGPT处理数据结构的问题

目录 使用ChatGPT进行数据格式化转换 代码示例 ChatGPT格式化数据提示语 代码示例 批量格式化数据提示语 代码示例 ChatGPT生成的格式化批处理代码 使用ChatGPT合并不同数据源的数据 合并数据提示语 自动合并数据提示语 ChatGPT生成的自动合并代码 结论 数据合并是…

1339. 分裂二叉树的最大乘积

链接&#xff1a; ​​​​​​1339. 分裂二叉树的最大乘积 题解&#xff1a; /*** Definition for a binary tree node.* struct TreeNode {* int val;* TreeNode *left;* TreeNode *right;* TreeNode() : val(0), left(nullptr), right(nullptr) {}* …

axi4 exclusive 原子操作

axi4 exclusive 原子操作 axi4

Java——左移、右移和其他特殊运算符

&#x1f61c;作 者&#xff1a;是江迪呀✒️本文关键词&#xff1a;微信小程序、页面跳转、移动端、前端☀️每日 一言&#xff1a;我宁要痛苦不要麻木&#xff01; 一、前言 当涉及位操作和位级运算时&#xff0c;Java 提供了一组特殊的运算符&#xff0c;即左移…

CSS自学框架之动画

这一节&#xff0c;自学CSS动画。主要学习了淡入淡出、淡入缩放、缩放、移动、旋转动画效果。先看一下成果。 优雅的过渡动画&#xff0c;为你的页面添加另一份趣味&#xff01; 在你的选择器里插入 animation 属性&#xff0c;并添加框架内置的 keyframes 即可实现&#xff0…

Typescript基础知识(类型拓宽、类型缩小)

系列文章目录 引入一&#xff1a;Typescript基础引入&#xff08;基础类型、元组、枚举&#xff09; 引入二&#xff1a;Typescript面向对象引入&#xff08;接口、类、多态、重写、抽象类、访问修饰符&#xff09; 第一章&#xff1a;Typescript基础知识&#xff08;Typescri…