C++第十四弹---模板初阶

 ✨个人主页: 熬夜学编程的小林

💗系列专栏: 【C语言详解】 【数据结构详解】【C++详解】

目录

1、泛型编程

 2、函数模板

2.1、函数模板的概念

2.2、函数模板的格式

2.3、函数模板的原理

2.4、函数模板的实例化

2.5、模板参数的匹配原则

3、类模板

3.1、类模板的定义格式

3.2、类模板的实例化

总结


1、泛型编程

什么是泛型编程呢?

在计算机程序设计领域,为了避免因数据类型的不同,而被迫重复编写大量相同业务逻辑的代码,人们发展的泛型及泛型编程技术。所以泛型,实质上就是不使用具体数据类型(例如 int、double、float 等),而是使用一种通用类型来进行程序设计的方法,该方法可以大规模的减少程序代码的编写量,让程序员可以集中精力用于业务逻辑的实现。

如何实现一个通用的交换函数呢?

如果按照我们C语言函数的实现,需要创建多个函数名不同的函数,但是我们学习了C++可以通过重载函数进行实现,创建多个函数,函数名相同,但是参数不同。

C++重载函数实现如下:

void Swap(int& left, int& right)
{
int temp = left;
left = right;
right = temp;
}
void Swap(double& left, double& right)
{
double temp = left;
left = right;
right = temp;
}
void Swap(char& left, char& right)
{
char temp = left;
left = right;
right = temp;
}
......

通过上面的代码我们可以发现使用函数重载虽然可以实现,但是有一下几个不好的地方

1. 重载的函数仅仅是类型不同,代码复用率比较低,只要有新类型出现时,就需要用户自己增加对应的函数。
2. 代码的可维护性比较低,一个出错可能所有的重载均出错。


那我们能否告诉编译器一个模子,让编译器根据不同的类型利用该模子来生成代码呢?


如果在C++中,也能够存在这样一个模具,通过给这个模具中填充不同材料(类型),来获得不同材料的铸件(即生成具体类型的代码),那将会节省许多头发。巧的是前人早已将树栽好,我们只需在此乘凉。

此处就可以用到我们C++的模板,也是泛型编程的基础。(模板包括函数模板和类模板)

 2、函数模板


2.1、函数模板的概念


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


2.2、函数模板的格式

template<typename T1, typename T2,......,typename Tn>
返回值类型 函数名(参数列表){}

//template、typename均为关键字,T为template的首字母大写,此处可以根据自己喜好选择字母,通常使用T+数字

template<typename T>
void Swap(T& left, T& right)
{
	T temp = left;
	left = right;
	right = temp;
}

注意:typename是用来定义模板参数关键字,也可以使用class(切记:不能使用struct代替class)


2.3、函数模板的原理


那么如何解决上面的问题呢?大家都知道,瓦特改良蒸汽机,人类开始了工业革命,解放了生产力。机器生产淘汰掉了很多手工产品。本质是什么,重复的工作交给了机器去完成。有人给出了论调:懒人创造世界。

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

在编译器编译阶段,对于模板函数的使用,编译器需要根据传入的实参类型来推演生成对应类型的函数以供调用。

比如:当用double类型使用函数模板时,编译器通过对实参类型的推演,将T确定为double类型,然后产生一份专门处理double类型的代码,对于字符类型也是如此。


2.4、函数模板的实例化

用不同类型的参数使用函数模板时,称为函数模板的实例化。模板参数实例化分为:隐式实例化和显式实例化。


1. 隐式实例化:让编译器根据实参推演模板参数的实际类型

template<class T>
T Add(const T& left, const T& right)
{
	return left + right;
}
int main()
{
	int a1 = 10, a2 = 20;
	double d1 = 10.0, d2 = 20.0;
	Add(a1, a2);// 编译器将类型推导为int 类型
	Add(d1, d2);// 编译器将类型推导为double 类型

	/*
	以下两条语句不能通过编译,因为在编译期间,当编译器看到该实例化时,需要推演其实参类型
	通过实参a1将T推演为int,通过实参d1将T推演为double类型,但模板参数列表中只有一个T,
	编译器无法确定此处到底该将T确定为int 或者 double类型而报错
	注意:在模板中,编译器一般不会进行类型转换操作,因为一旦转化出问题,编译器就需要背黑锅*/
	Add(a1, d1);
    Add(a2,d1);
	
	// 此时有两种处理方式:1. 用户自己来强制转化 2. 使用显式实例化
	Add(a1, (int)d1);// 强制类型转化
    Add((double)a2,d1);// 强制类型转化
	return 0;
}

 2. 显式实例化:在函数名后的<>中指定模板参数的实际类型

template<class T>
T Add(const T& left, const T& right)
{
	return left + right;
}
int main(void)
{
	int a = 10;
	double b = 20.0;
	// 显式实例化  尖括号内部确定模板的类型
	Add<int>(a, b);
	return 0;
}

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


2.5、模板参数的匹配原则


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

// 专门处理int的加法函数
int Add(int left, int right)
{
	return left + right;
}
// 通用加法函数
template<class T>
T Add(T left, T right)
{
	return left + right;
}
void Test()
{
	Add(1, 2);      // 与非模板函数匹配,编译器不需要特化,直接调用int类型加法函数
	Add<int>(1, 2); // 使用显示实例化函数,调用编译器特化的Add版本
}

此处可以通过调试进行测试,在第一个函数处打一个断点(F9),然后F11进入函数,就能直接进入int类型的专门函数,显示实例化也是同理。 

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

// 专门处理int的加法函数
int Add(int left, int right)
{
	return left + right;
}
// 通用加法函数
template<class T1, class T2>
T1 Add(T1 left, T2 right)
{
	return left + right;
}
void Test()
{
	Add(1, 2); // 与非函数模板类型完全匹配,调用非模板函数
	Add(1, 2.0); // 模板函数可以生成更加匹配的版本,编译器根据实参生成更加匹配的Add函数
}

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

总结:与非模板函数参数类型完全匹配先调用非模板函数,其余都调用模板函数。


3、类模板


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;
}

3.2、类模板的实例化

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

// Vector类名,Vector<int>才是类型
Vector<int> s1;
Vector<double> s2;

注意:此章节只是大致认识模板,后面会通过STL详细讲解模板的用法。 

总结


本篇博客就结束啦,谢谢大家的观看,如果公主少年们有好的建议可以留言喔,谢谢大家啦!

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

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

相关文章

三、强一致性介绍

这里写自定义目录标题 三、强一致性介绍3.1 基本理解3.2 DTP模型3.3 落地协议XA3.4 ⼆阶段提交模型3.5 ⼆阶段提交的问题3.6 navicat操作xa 三、强一致性介绍 3.1 基本理解 相关特点 强⼀致性解决⽅案要求在任何时间点&#xff0c;任何时刻查询&#xff0c;参与全局事务的各个…

B树、B+树、哈夫曼树

目录 1. B树2. B树3. 哈夫曼树 1. B树 特点&#xff1a;一个节点当中可以有多个值&#xff0c;节点内部key 值是有序的&#xff0c;节点内部存储的是key-value类型的数据 磁盘中文件存储用B树。 4阶B树一个节点最多三个key值 5阶B树一个节点最多四个key值 B树有很多的分支&…

抽象类和接口(2)(接口部分)

❤️❤️前言~&#x1f973;&#x1f389;&#x1f389;&#x1f389; hellohello~&#xff0c;大家好&#x1f495;&#x1f495;&#xff0c;这里是E绵绵呀✋✋ &#xff0c;如果觉得这篇文章还不错的话还请点赞❤️❤️收藏&#x1f49e; &#x1f49e; 关注&#x1f4a5;&a…

Go的数据结构与实现【Ring Buffer】

介绍 在本文中&#xff0c;我们将用Go实现环形缓冲区&#xff08;Ring Buffer&#xff09; Ring Buffer 环形缓冲区&#xff08;或循环缓冲区&#xff09;是一种有界循环数据结构&#xff0c;用于在两个或多个线程之间缓冲数据。当我们继续写入环形缓冲区时&#xff0c;它会…

JavaScript 入门指南(三)BOM 对象和 DOM 对象

BOM 对象 BOM 简介 BOM&#xff08;browser Object Model&#xff09;即浏览器对象模型BOM 由一系列对象组成&#xff0c;是访问、控制、修改浏览器的属性的方法BOM 没有统一的标准&#xff08;每种客户端都可以自定标准&#xff09;。BOM 的顶层是 window 对象 window 对象 …

深入解析Hadoop生态核心组件:HDFS、MapReduce和YARN

这里写目录标题 01HDFS02Yarn03Hive04HBase1&#xff0e;特点2&#xff0e;存储 05Spark及Spark Streaming关于作者&#xff1a;推荐理由&#xff1a;作者直播推荐&#xff1a; 一篇讲明白 Hadoop 生态的三大部件 进入大数据阶段就意味着进入NoSQL阶段&#xff0c;更多的是面向…

代码随想录阅读笔记-二叉树【二叉树的所有路径】

题目 给定一个二叉树&#xff0c;返回所有从根节点到叶子节点的路径。 说明: 叶子节点是指没有子节点的节点。 示例: 思路 这道题目要求从根节点到叶子的路径&#xff0c;所以需要前序遍历&#xff0c;这样才方便让父节点指向孩子节点&#xff0c;找到对应的路径。 在这道…

【CSS】基础选择器

目录 标签选择器 id选择器 类选择器 CSS的编写地点&#xff1a; 标签选择器 说明&#xff1a;标签选择器实际上就是HTML标签元素&#xff08;可以是任何HTML元素&#xff09;&#xff0c;用来改变一个指定标签的样式 示例&#xff1a; <style type"text/css"…

QQ邮箱SMTP发送邮件时要注意哪些安全设置?

QQ邮箱SMTP发送邮件的步骤&#xff1f;如何配置QQ邮箱服务器&#xff1f; 在使用QQ邮箱SMTP发送邮件时&#xff0c;安全设置是至关重要的一环。不当的安全设置不仅可能导致邮件发送失败&#xff0c;还可能使你的账户面临安全风险。下面&#xff0c;AokSend就来详细探讨一下QQ邮…

基于单片机16位智能抢答器设计

**单片机设计介绍&#xff0c;基于单片机16位智能抢答器设计 文章目录 一 概要二、功能设计设计思路 三、 软件设计原理图 五、 程序六、 文章目录 一 概要 基于单片机16位智能抢答器设计是一个结合了单片机技术、显示技术、按键输入技术以及声音提示技术的综合性项目。其设计…

脑机辅助推导算法

目录 一&#xff0c;背景 二&#xff0c;华容道中道 1&#xff0c;问题 2&#xff0c;告诉脑机如何编码一个正方形格子 3&#xff0c;让脑机汇总信息 4&#xff0c;观察图&#xff0c;得到启发式算法 5&#xff0c;根据启发式算法求出具体解 6&#xff0c;可视化 一&am…

苹果App审核大揭秘

苹果上架要求是苹果公司对于提交应用程序到苹果商店上架的要求和规定。这些要求主要是为了保证用户体验、应用程序的质量和安全性。以下是苹果上架要求的详细介绍&#xff1a;1. 应用程序的内容和功能必须符合苹果公司的规 苹果上架要求是苹果公司对于提交应用程序到苹果商店上…

u盘不显示盘符怎么办,u盘不显示盘符

我们经常使用电脑,难免会遇到各种问题,其中U盘不显示盘盘符也是常见的一种。用u盘插入电脑usb接口后,却识别不出u盘,而且更换usb接口以后还是没有u盘盘符,这可怎么用呢?针对此问题,极客狗整理了两个处理方法,接下来带小伙伴一起看看u盘不显示盘符怎么办。遇到同样问题的…

Python数据结构实验 查找实验(一)

一、实验目的 1&#xff0e;熟悉查找的基本概念&#xff0c;包括静态查找表和动态查找表、内查找和外查找之间的差异以及平均查找长度等&#xff1b; 2&#xff0e;掌握线性表上的各种查找算法&#xff0c;包括顺序查找、折半查找和分块查找的基本思路、算法实现和查找效率等…

游戏引擎中的声音系统

一、声音基础 1.1 音量 声音振幅的大小 压强p&#xff1a;由声音引起的与环境大气压的局部偏差 1.2 音调 1.3 音色 1.4 降噪 1.5 人的听觉范围 1.6 电子音乐 将自然界中连续的音乐转换成离散的信号记录到内存中 采样 - 量化 - 编码 香农定理&#xff1a;采样频率是信…

云原生技术精选:探索腾讯云容器与函数计算的最佳实践

文章目录 写在前面《2023腾讯云容器和函数计算技术实践精选集》深度解读案例集特色&#xff1a;腾讯云的创新实践与技术突破精选案例分析——Stable Diffusion云原生部署的最佳实践精选集实用建议分享总结 写在前面 在数字化转型的浪潮下&#xff0c;云计算技术已成为企业运营…

shell脚本发布docker-nginx vue2 项目示例

docker、git、node.js安装略过。 使git pull或者git push不需要输入密码操作方法 nginx安装在docker容器里面&#xff0c;参见&#xff1a;https://blog.csdn.net/HSJ0170/article/details/128631155 姊妹篇&#xff08;宿主机nginx&#xff0c;非docker-nginx&#xff09;&am…

Real-data WRF | setup and run and experiment

前言 Parent Model 用于初始化和边界条件的网格数据 GFS/FNL、NAM、RAP/HRRR、重新分析&#xff08;NARR、CFSR、NNRP、ERA-interim、ERA5 等&#xff09;、其他 WRF 运行 WPS WRF 预处理系统&#xff08;由 geogrid、ungrib 和 metgrid 程序组成&#xff09; WRF 模拟几…

【Linux多线程】生产者消费者模型

【Linux多线程】生产者消费者模型 目录 【Linux多线程】生产者消费者模型生产者消费者模型为何要使用生产者消费者模型生产者消费者的三种关系生产者消费者模型优点基于BlockingQueue的生产者消费者模型C queue模拟阻塞队列的生产消费模型 伪唤醒情况&#xff08;多生产多消费的…

【手册】——mq延迟队列

目录 一、背景介绍二、思路&方案三、过程1.项目为啥用延迟队列&#xff1f;2.项目为啥用三方延迟队列&#xff1f;3.项目中为啥用rabbitmq延迟队列&#xff1f;4.rabbitmq延迟队列的安装5.rabbitmq的延迟队列配置方式5.1.exchange配置5.2.queues配置5.3.exchange和queues的…