C++模板—函数模板、类模板

 目录

一、函数模板

1、概念

2、格式

3、实例化

4、模板参数的匹配 

二、类模板

1、定义格式

2、实例化


交换两个变量的值,针对不同类型,我们可以使用函数重载实现。
void Swap(double& left, double& right)
{
	double tmp = left;
	left = right;
	right = tmp;
}
void Swap(int& left, int& right)
{
	int tmp = left;
	left = right;
	right = tmp;
}
使用函数重载虽然可以实现,但是有一下几个不好的地方:
  • 重载的函数仅仅是类型不同,代码复用率比较低,只要有新类型出现时,就需要用户自己增加对应的函数。
  • 代码的可维护性比较低,一个出错可能所有的重载均出错。

C++中提供了一种新的方式——泛型编程:编写与类型无关的通用代码,是代码复用的一种手段。模板是泛型编程的基础。

一、函数模板

1、概念

函数模板代表了一个函数家族,该函数模板与类型无关,在使用时被参数化,根据实参类型产生函数的特定类型版本。

2、格式

template<typename T1, typename T2,......,typename Tn>

返回值类型 函数名(参数列表){ },typename是用来定义模板参数关键字,也可以使用class(切记:不能使用struct代替class)。

交换变量值的函数使用函数模板如下:

template<class T>
void Swap(T& x, T& y)
{
	T tmp = x;
	x = y;
	y = tmp;
}

简化过程的模板实际上是编译器帮我们处理了复杂的过程。

在编译器编译阶段,对于模板函数的使用,编译器需要根据传入的实参类型来推演生成对应类型的函数以供调用。比如:当用double类型使用函数模板时,编译器通过对实参类型的推演,将T确定double类型,然后产生一份专门处理double类型的代码,对于字符类型也是如此。

 

 定义多个模板数:

template<class A,class B>
void Fun{}

其实库里包含了swap函数,我们以后可以直接用。

int main()
{
	int a = 1, b = 2;
	swap(a, b);

	double c = 1.1, d = 2.22;
	swap(a, b);

	return 0;
}

3、实例化

函数模板的实例化是指根据函数模板创建具体的函数实例,实例化函数模板的过程是将函数模板中的类型参数替换为实际的类型,并生成对应的函数定义。这样就可以根据不同的类型参数创建多个函数实例,每个实例都可以处理相应类型的数据。

模板参数实例化分为:隐式实例化和显式实例化
template<class T>
T Add(const T& left, const T& right)
{
	return left + right;
}

int main()
{
	int a1 = 10, a2 = 20;
	double d1 = 10.11, d2 = 20.22;

	Add(a1, a2);
	Add(d1, d2);
	Add(a1, d1);

	return 0;
}

前两个相同类型可以正常编译,但模板参数类型不同时编译出现错误。

这时因为在编译期间,当编译器看到该实例化时,需要推演其实参类型通过实参a1将T推演为int,通过实参d1将T推演为double类型,但模板参数列表中只有一个T, 编译器无法确定此处到底该将T确定为int 或者 double类型而报错,在模板中,编译器一般不会进行类型转换操作。

我们可以选择强制类型转换解决,或者显示实例化。

 方法一:显式类型转换,实参传递给形参,自动推演模板类型。

template<class T>
T Add(const T& left, const T& right)
{
	return left + right;
}

int main()
{
	int a1 = 10, a2 = 20;
	double d1 = 10.11, d2 = 20.22;

	cout << Add(a1, (int)d1) << endl;//显示类型转换
	cout << Add((double)a1, d1) << endl;

	return 0;
}

 

方法二:显示实例化,在函数名和参数列表中间加上模板参数,参数隐式类型转换。

如果类型不匹配,编译器会尝试进行隐式类型转换,如果无法转换成功编译器将会报错 

template<class T>
T Add(const T& left, const T& right)
{
	return left + right;
}

int main()
{
	int a1 = 10, a2 = 20;
	double d1 = 10.11, d2 = 20.22;

	cout << Add<int>(a1, d1) << endl;//隐式类型转换
    cout << Add<double>(a1, d1) << endl;

	return 0;
}

4、模板参数的匹配 

1、一个非模板函数可以和一个同名的函数模板同时存在,而且该函数模板还可以被实例化为这个非模板函数。

下面这两个函数是可以同时存在的。 

int Add(int left, int right)
{
	return left + right;
}

template<class S>
S Add(S left,S right)
{
	return left + right;
}

int main()
{
	Add(1, 2); 
	
	return 0;
}

 在Add函数调用处打断点,我们试着观察一下到底会调用哪个函数。

 

 可以发现,Add函数选择了专门处理int的加法函数。

 

2、对于非模板函数和同名函数模板,如果其他条件都相同,在调动时会优先调用非模板函数而不会从该模板产生出一个实例。如果模板可以产生一个具有更好匹配的函数, 那么将选择模板。

我们也可以通过显示实例化调用函数模板。 

 

3、模板函数不允许自动类型转换,但普通函数可以进行自动类型转换

  • 在函数模板中,对于函数参数的类型推断是严格按照实参的类型进行匹配的。如果实参的类型与函数模板参数的类型不完全匹配,编译器将无法进行自动类型转换来匹配函数模板的参数类型。
  • 相比之下,普通函数可以进行自动类型转换。当调用普通函数时,如果实参的类型与函数参数的类型不完全匹配,编译器会尝试进行自动类型转换,以便匹配函数参数的类型。这种自动类型转换可以是隐式的,也可以是通过类型转换操作符进行显式的。

二、类模板

 1、定义格式

​
template<class T1, class T2, ..., class Tn>
class 类模板名
{
	// 类内成员定义
};
// 动态顺序表
// 注意: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;
}

​

在这段代码中,`Vector` 是一个类模板,它并不是一个具体的类,而是一个用于生成具体类的模具或蓝图。当我们使用 `Vector` 类模板时,需要提供具体的类型参数,例如 `Vector<int>` 或 `Vector<double>`,编译器会根据这些类型参数生成对应的具体类。

类模板的定义中使用了模板参数 `T`,它表示一个占位符类型,可以在实例化时被具体的类型替换。在这个例子中,`T` 表示动态顺序表中存储的元素类型。

通过实例化类模板,编译器会根据模板定义生成具体的类,其中的成员函数和成员变量的类型会被替换为实际的类型。例如,`Vector<int>` 实例化后的类将具有 `int* _pData`、`size_t _size` 和 `size_t _capacity` 成员变量,以及相应的成员函数。

因此,`Vector` 并不是一个具体的类,而是一个用于生成具体类的模板。每次使用不同的类型参数实例化 `Vector`,都会生成一个独立的具体类,用于处理特定类型的数据。这样可以提供代码的灵活性和重用性,使得我们可以使用相同的代码逻辑处理不同类型的数据。

2、实例化

类模板实例化与函数模板实例化不同,类模板实例化需要在类模板名字后跟<>,然后将实例化的类型放在<> 中即可,类模板名字不是真正的类,而实例化的结果才是真正的类
Vector类名,Vector<int>才是类型
Vector<int> s1;
Vector<double> s2;

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

传统算法: Pygame 实现快速排序

使用 Pygame 模块实现了快速排序的动画演示。首先,它生成一个包含随机整数的数组,并通过 Pygame 在屏幕上绘制这个数组的条形图。接着,通过快速排序算法对数组进行排序,动画效果可视化每一步的排序过程。在排序的过程中,程序选择一个基准元素(pivot),将数组分成两部分,…

C语言每日一题(43)旋转链表

力扣 61 旋转链表 题目描述 给你一个链表的头节点 head &#xff0c;旋转链表&#xff0c;将链表每个节点向右移动 k 个位置。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5], k 2 输出&#xff1a;[4,5,1,2,3]示例 2&#xff1a; 输入&#xff1a;head [0,1,2], …

Java中的异常你了解多少?

目录 一.认识异常二.异常分类三.异常的分类1.编译时异常2.运行时异常 四.异常的处理1.LYBL&#xff1a;事前防御型2.EAFP&#xff1a;事后认错型 五.异常的抛出Throw注意事项 六.异常的捕获1.异常的捕获2.异常声明throws3.try-catch捕获并处理 七.自定义异常 一.认识异常 在Jav…

操作系统进程与线程篇

目录 一、进程 1.1、进程状态 1.2、进程的控制结构 1.3、进程的控制 1.4、进程的上下文切换 二、线程 2.1.线程是什么 2.2、线程与进程的比较 2.3、线程的上下文切换 2.4、线程的实现 2.5、轻量级线程 三、进程间的通信方式 3.1、管道 3.2、消息队列 3.3、共享内…

vscode插件问题

1 Vscode code颜色变化 最外层标签颜色变成白色 其他标签有颜色&#xff0c;css代码颜色有些变成白色 是安装的另一个插件vue影响的&#xff0c;卸载就能恢复正常的颜色 2 配置Vue项目的代码片段 css 样式代码片段 配置css.json上后偶尔能用偶尔不能用&#xff0c;Vscode 右下…

opencv 图像边框

cv.copyMakeBorder() 图像设置边框或者填充

良品铺子“降价不降质”:利他主义,零食新成长逻辑

最近&#xff0c;男大学生组团穿军大衣&#xff0c;女大学生集体穿花棉袄&#xff0c;火遍全网。 相当一批大学生发现&#xff0c;军大衣、花棉袄在保暖上不输羽绒服&#xff0c;而且价格还便宜。这股风潮背后&#xff0c;其实映射出当下年轻人在消费上变得愈发&#xff1a; …

一种excel多线程并发写sheet的方案

一、背景 有一次项目的需求要求导出excel&#xff0c;并且将不同的数据分别写到不同的sheet中。 二、 方案概述 首先一开始使用easyexcel去导出excel&#xff0c;结果发现导出时间需要3秒左右。于是想着能不能缩短excel导出时间&#xff0c;于是第一次尝试使用异步线程去查询数…

什么是JVM的内存模型?详细阐述Java中局部变量、常量、类名等信息在JVM中的存储位置

导航&#xff1a; 【Java笔记踩坑汇总】Java基础JavaWebSSMSpringBootSpringCloud瑞吉外卖/黑马旅游/谷粒商城/学成在线设计模式面试题汇总性能调优/架构设计源码-CSDN博客 目录 一、JVM基本介绍 二、JVM内存模型 2.0 概述 2.1 类加载子系统 2.2 运行时数据区 2.2.0 基本…

【C++笔记】红黑树封装map和set

一、map和set的泛型封装逻辑 map和set的底层都是红黑树&#xff0c;所以我们想要用红黑树封装map和set的第一个问题就来了&#xff0c;因为set是key结构而map是key-value结构&#xff0c;怎样用同一个底层结构去封装出两个不同存储结构的容器呢&#xff1f;难道我们要将红黑树…

算法题--排椅子(贪心)

题目链接 code #include<bits/stdc.h> using namespace std;struct node{int indx;//用来存储数组下标int cnt;//用来计数 };bool cmp(node a,node b){ //判断是否是数字最大的一个就是经过最多谈话人的道return a.cnt>b.cnt; } node row[2010],cow[2010];bool cmp…

LLM算法工程师面试题总结

一、请简述对大模型的基本原理和架构的理解。 大型语言模型如GPT&#xff08;Generative Pre-trained Transformer&#xff09;系列是基于自注意力机制的深度学习模型&#xff0c;主要用于处理和生成人类语言。下面简要概述了它们的一些基本原理和架构特点&#xff1a; 基本原…

C/C++---------------LeetCode第876. 链表的中间结点

链表的中间结点 题目及要求双指针在main内使用 题目及要求 给你单链表的头结点 head &#xff0c;请你找出并返回链表的中间结点。 如果有两个中间结点&#xff0c;则返回第二个中间结点。 示例 1&#xff1a; 示例 2&#xff1a; 双指针 思路&#xff1a;分别定义快慢指针…

「Qt Widget中文示例指南」如何创建一个计算器?(二)

Qt 是目前最先进、最完整的跨平台C开发工具。它不仅完全实现了一次编写&#xff0c;所有平台无差别运行&#xff0c;更提供了几乎所有开发过程中需要用到的工具。如今&#xff0c;Qt已被运用于超过70个行业、数千家企业&#xff0c;支持数百万设备及应用。 本文将展示如何使用…

淘宝/天猫商品详情API接口丨京东商品详情丨1688商品详情丨接口key密钥获取方式

要获取淘宝/天猫商品详情API接口、京东商品详情API接口、1688商品详情API接口以及接口密钥&#xff08;Key&#xff09;&#xff0c;可以按照以下步骤进行操作&#xff1a; 注册并登录淘宝/天猫开发者中心或京东开放平台或1688开放平台&#xff0c;并创建应用。在创建应用的过…

Human3.6m数据处理(mhformer代码解读)

对于3d人体姿态估计任务中数据集human3.6m的处理 写在最前面&#xff1a;这是我自己的理解&#xff0c;说的不一定对。 human3.6m有很多格式的数据&#xff0c;包括视频、2d ground truth、3d ground truth&#xff0c;还分为xyz坐标的表示形式和旋转向量表示形式&#xff0c;这…

HarmonyOS应用开发者基础认证考试(98分答案)

基于最近大家都在考这个应用开发者基础认证考试&#xff0c;因此出了一期&#xff0c;一样复制word里面搜索做&#xff0c;很快&#xff0c;当然good luck 判断题 Ability是系统调度应用的最小单元,是能够完成一个独立功能的组件。一个应用可以包含一个或多个Ability。 正确(Tr…

Swift构造器继承链

类类型的构造器代理 Swift构造器需遵循以下三大规则&#xff1a; 指定构造器必须调用它直接父类的指定构造器方法便利构造器必须调用同一个类中定义的其他初始化方法便利构造器在最后必须调用一个指定构造器 两段式构造过程 Swift 中类的构造过程包含两个阶段。第一个阶段&a…

Redis主从与哨兵架构详解

目录 主从架构 主从环境搭建 主从复制流程 1. 全量复制 2. 部分复制 主从风暴 哨兵架构 概念 哨兵环境搭建 主从架构 主从环境搭建 1. 复制一份redis.conf文件, 修改下面几行配置 port 6380 pidfile /var/run/redis_6380.pid logfile "6380.log" dir /usr/…

PowerDesigner数据库建模软件的安装

解压&#xff1a; 解压好以后&#xff0c;点击PowerDesigner.exe安装 这个安装的版本是15 选择安装路径&#xff0c;可以默认可以自定义&#xff1a; 直接点next&#xff1a; 全选了 点击next&#xff1a; 点击next&#xff1a; 点finish 汉化&#xff1a; 先把pojie和汉化文件…