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

news2024/11/28 0:34:20

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

💗系列专栏: 【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详细讲解模板的用法。 

总结


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

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

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

相关文章

【学习】如何成为资深的软件测试工程师“大神”?

一个优秀的软件测试工程师不仅需要有深厚的技术知识和经验&#xff0c;还需要有良好的沟通能力、分析能力和问题解决能力。总的来说&#xff0c;一个"大神"一样的软件测试工程师应该是一个全面的技术专家&#xff0c;同时还需要有出色的沟通和问题解决能力&#xff0…

Mac 版 IDEA 中配置 GitLab

一、安装Git 在mac终端输入Git检测指令&#xff0c;可以通过git命令查看Git是否安装过&#xff0c;如果没有则会弹出安装按钮&#xff0c;如果安装过则会输出如下信息。 WMBdeMacBook-Pro:~ WENBO$ git usage: git [--version] [--help] [-C <path>] [-c namevalue][--…

ubuntu23.10配置RUST开发环境

系统版本: gcc版本 下载rustup安装脚本: curl --proto https --tlsv1.2 https://sh.rustup.rs -sSf | sh下载完成后会自动执行 选择默认安装选项 添加cargo安装目录到环境变量 vim ~/.bashrc 默认已添加 使用环境变量立即生效 source ~/.bashrc 执行rust开发环境,在终端输入…

深度剖析:计算机集群在大数据体系中的关键角色和技术要点

什么是计算机集群&#xff1f; 计算机集群是一组相互连接的计算机&#xff08;服务器&#xff09;&#xff0c;它们协同工作以完成共同的任务。集群中的每个计算机节点都可以独立运行&#xff0c;但它们通过网络连接在一起&#xff0c;以实现更高的可靠性、性能和可扩展性。 典…

给虚拟机配置静态IP并使用FileZIlla在虚拟机和Windows之间传输文件(ssh和ftp两种方法)

一、配置操作系统网络 &#x1f338;下面的步骤主要是配置虚拟机的静态IP&#xff0c;方便后续用 FikeZilla 在windows和虚拟机之间传输文件&#xff08;否则用默认的ip分配方案为 DHCP ,每一次开机时的ip都是有可能不同的,这样就会导致每次远程连接都需要查看ip地址.&#xf…

Python | 非规则矩形投影添加斑马线边框

前言 在地图绘制领域&#xff0c;非规则投影的示例相对较少&#xff0c;通过几个python的示例可以更好地理解如何在不同投影类型和边界形状下绘制地图。 以下提供了一系列示例&#xff0c;演示了如何在地图中添加非规则边界和边框。这些示例涵盖了不同的投影类型和边界形状&a…

element-ui 表单校验,失去焦点/框内值改变,校验

前提&#xff1a;在el-form表单中&#xff0c;框中有值&#xff0c;失去焦点或者框内值改变的时候&#xff0c;校验提示&#xff0c;依旧没有消失el-select校验失效问题 之前el-select&#xff0c;trigger时候用的“blur”,导致失效&#xff0c;现在 el-select 统一改为"c…

PyTorch深度学习实战(40)——零样本学习(Zero-Shot Learning)

PyTorch深度学习实战&#xff08;40&#xff09;——零样本学习 0. 前言1. 零样本学习2. 实现零样本学习模型2.1 模型分析2.2 构建零样本学习模型 小结系列链接 0. 前言 零样本学习 (Zero-Shot Learning) 是一种机器学习方法&#xff0c;旨在解决传统监督学习中&#xff0c;当…

pulsar存在大量消费未ack的原因

问题起源&#xff1a; 某产品灰度上线后&#xff0c;从pulsar服务端监控发现存在一种现象&#xff1a;消费但未ack的信息不断增加&#xff0c;直到3000左右就稳定下来了且消费速度为0&#xff0c;但不清楚这3000是怎么来的&#xff0c;因为代码是消费到立马ack的&#xff1b; …

格雷希尔G10系列L150A和L200A气动快速连接器,在新能源汽车线束线缆剥线后的气密性测试密封方案

线束线缆在很多用电环境都有使用&#xff0c;比如说新能源汽车&#xff0c;从电池包放电开始&#xff0c;高低压、通讯都开始进行工作&#xff0c;线束在连接的地方需要具有较高的气密性和稳定性&#xff0c;才能保证车辆在不同环境下能够正常的运行。 线束在组装铜鼻子前需要剥…

【Oracle篇】expdp/impdp高效完成全部生产用户的全库迁移(第四篇,总共四篇)

☘️博主介绍☘️&#xff1a; ✨又是一天没白过&#xff0c;我是奈斯&#xff0c;DBA一名✨ ✌✌️擅长Oracle、MySQL、SQLserver、Linux&#xff0c;也在扩展大数据方向的知识面✌✌️ ❣️❣️❣️大佬们都喜欢静静的看文章&#xff0c;并且也会默默的点赞收藏加关注❣️❣️…

基于SpringBoot的游戏商城系统的设计与实现(论文+源码)_kaic

目录 1前言 1.1研究的背景及意义 1.2国内外的研究状况和发展趋势 2需求分析 2.1系统需求分析 2.1.1技术可行性 2.1.2经济可行性 2.1.3操作可行性 2.2系统的开发环境 2.2.1 Springboot框架 2.2.2 数据库Mysql 2.2.3 IntelliJ IDEA平台 2.2.4 Mybatis和MyBatis-plus 2.2.5 前端框…

火鸟门户同城模块

同城活动 同城活动是指在同一城市举办的活动&#xff0c;可以是多种类型&#xff0c;例如&#xff1a; 聚会&#xff1a;朋友聚会、同学聚会、兴趣爱好聚会等。展览&#xff1a;艺术​​展览、科技展览、文化展览等。演出节目&#xff1a;演唱会、音乐会、戏剧表演等。比赛项…

JumpServer 堡垒主机

JumpServer 堡垒机帮助企业以更安全的方式管控和登陆各种类型的资产 SSH&#xff1a;Linux/Unix/网络设备等Windows&#xff1a;Web方式连接/原生RDP连接数据库&#xff1a;MySQL、Oracle、SQLServer、PostgreSQL等Kubernetes&#xff1a;连接到K8s集群中的PodsWeb站点&#x…

Backend - gitea 首次建库(远端本地)

目录 一、建立远端储存库 1. 进入新增画面 2. 填写储存库名称&#xff08;如book&#xff09;&#xff0c;点击“建立”即可 二、本地关联远端储存库 1. 本地初始化储存库代码 &#xff08;1&#xff09;新建文件夹 &#xff08;2&#xff09;获取远端储存库 2. 本地编写…

前端学习<二>CSS基础——14-CSS3属性详解:Web字体

前言 开发人员可以为自已的网页指定特殊的字体&#xff08;将指定字体提前下载到站点中&#xff09;&#xff0c;无需考虑用户电脑上是否安装了此特殊字体。从此&#xff0c;把特殊字体处理成图片的方式便成为了过去。 支持程度比较好&#xff0c;甚至 IE 低版本的浏览器也能…

HTML期末作业-香水网站,逐步讲解

知名品牌 CHANEL I wear nothing but a few drops of Chanel No.5. 了解更多 GIVENCHY 纪梵希香水几乎就是赫本本人的化身——经典、优雅、高贵、简洁、女性化 了解更多 DIOR Dior Addict the now fragrance from Dior. 了解更多 BURBUEEY The good things in life neve…

大数据技术之 Apache Doris(一)

第 1 章 Doris 简介 1.1 Doris 概述 Apache Doris 由百度大数据部研发&#xff08;之前叫百度 Palo&#xff0c;2018 年贡献到 Apache 社区后&#xff0c;更名为 Doris &#xff09;&#xff0c;在百度内部&#xff0c;有超过 200 个产品线在使用&#xff0c;部署机器超过 10…

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

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

微信公众号账号迁移主体怎么变更?

公众号迁移有什么作用&#xff1f;只能变更主体吗&#xff1f;大家都知道&#xff0c;公众号是不支持直接变更主体的&#xff1b;但是很多情况下&#xff0c;我们又不得不进行账号主体的更换&#xff1b;这时候&#xff0c;我么就可以通过账号迁移功能&#xff0c;将A公众号的粉…