【C++深入浅出】模版初识

news2025/2/26 14:15:22

 


目录

一. 前言

二. 泛型编程

三. 函数模版 

3.1 函数模版的概念

3.2 函数模版的格式

3.3 函数模版的原理

3.4 函数模板的实例化

3.5 模板参数的匹配原则

四. 类模版

4.1 类模版的定义

4.2 类模版的实例化


一. 前言

        本期我们要介绍的是C++的又一大重要功能----模版。通过模版,我们可以很轻松的进行泛型编程,大大简化我们编程时的代码。

        本文的目标是让读者对模版有一定程度上的了解,以便后续STL的学习,对于模版更深层次的内容,我们放到以后再进行拓展。

        话不多说,开启我们今日的学习叭

二. 泛型编程

        假如现在有个需求,要求我们实现一个swap函数用于数据的交换,数据类型可能是整形浮点型字符型等等,通过我们之前学习的知识,你会如何进行实现呢?

        聪明的你可能会这样实现

//利用函数重载实现三种不同类型的swap函数
void Swap(int& x, int& y) //引用传参
{
	int tmp = x;
	x = y;
	y = tmp;
}

void Swap(double& x, double& y)
{
	double tmp = x;
	x = y;
	y = tmp;
}

void Swap(char& x, char& y)
{
	double tmp = x;
	x = y;
	y = tmp;
}

        是的,上面的代码确实实现了我们的需求,但还是存在一些不足,体现在如下两个方面:

  1. 几个重载函数仅仅是类型不同,代码逻辑都是一样的,代码的复用率比较低。每当有新类型出现时,用户就需要再自行添加一个函数
  2. 代码的可维护性比较低,一个出错可能所有的重载均出错(想必你写的时候也是CV的叭)

        依照以上两点不足,那我们能不能只给编译器提供一个模板,到时候让编译器根据我们指定的类型自动生成所需要的函数呢?

        答案是有的。C++为我们提供了模版的概念,支持我们进行泛型编程。模版就好比一个模具,我们可以从倒入任何种类(类型)的东西到模具中,最终都会形成我们想要的对应图案(即生成相应的代码)

        泛型编程:编写与类型无关的通用代码,是代码复用的一种手段。模版是泛型编程的基础。模板分为函数模版类模版

三. 函数模版 

3.1 函数模版的概念

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

3.2 函数模版的格式

        函数模版的定义格式如下所示:

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

        例如,我们将上面的swap函数定义为函数模版如下:

template<typename T> //T是类型名
void swap(T& x, T& y)
{
	T tmp = x;
	x = y;
	y = tmp;
}

其中,typename是定义模版参数的关键字,也可以使用class关键字替代。

3.3 函数模版的原理

        我们可以把函数模板看做一个蓝图,它本身并不是一个函数,而是编译器根据调用函数时传入实参产生特定类型函数的一个模具。

        例如,当我们给swap函数传入double类型的参数,编译器就会自动根据模板生成一份参数为double类型的swap函数;而如果我们传入的是int类型的参数,编译器就会生成一份参数为int类型的swap函数。

        由此可见,模板就是将本来应该我们做的重复的事情交给了编译器去做,将手动敲出来的代码变为编译器自动生成。

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

3.4 函数模板的实例化

        当不同类型的参数使用函数模板时,编译器根据模版为这个类型生成一份函数代码,这个过程称为函数模板的实例化。模板参数实例化分为:隐式实例化显式实例化

        隐式实例化

        即编译器根据实参的类型自动推导模版参数的类型:

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

int main()
{
	double d1 = 2.0;
	double d2 = 5.0;
	Add(d1, d2); //d1和d2是double类型的,编译时编译器推导T为double类型,实例化double类型的Add函数

	int i1 = 0;
	Add(i1, d2); //此处编译会报错,因为Add只有一个模板参数T,而实参有两种类型,编译器推导时不知道将T推导为int还是double
	return 0;
}

         可以看到,编译器进行隐式实例化也不是胡乱实例化的,必须要有唯一的结果编译器才会进行实例化,编译器一般不会进行类型转换操作,因为一旦转化出问题,编译器就需要背黑锅。

        要解决上面Add函数的问题,我们有两个解决方法:

        1、用户自行进行强制类型转换

Add(i1, (int)d2); //将两个实参都变为int类型

Add((double)i1, d2); //将两个实参都变为double类型

        2、使用下面的显式实例化

        显式实例化

        调用函数时我们可以在函数名后使用<>来指定模板参数的实际类型,如下:

int main()
{
	int a = 10;
	double b = 20.0;
	double c = 30.0;
	// 显式实例化
	Add<int>(a, b);  //显式指定T实例化为int
	Add<double>(b, c);  //显式指定T实例化为double
	return 0;
}

        当实例化的函数形参和实参的类型不匹配时,编译器就会尝试进行隐式类型转换,如果无法转换成功编译器将会报错

template<typename T>
void Swap(T& x, T& y)
{
	T tmp = x;
	x = y;
	y = tmp;
}
int main()
{
	int i = 10;
	double d = 10.0;
	Swap<int>(i, d); //尽管我们显式进行实例化了,但这里编译器仍然会报错,原因是形参和实参无法进行隐式类型转换
	return 0;
}

3.5 模板参数的匹配原则

        1、 一个非模板函数可以和一个同名的函数模板同时存在,并且该函数模板还可以被实例化为这个非模板函数。换句话说,我们既可以使用非模板函数,也可以使用编译器特化的模板函数。

//Swap函数模板
template<class T>
void Swap(T& x, T& y)
{
	T tmp = x;
	x = y;
	y = tmp;
}

//处理int类型的Swap函数
void Swap(int& x, int& y)
{
	int tmp = x;
	x = y;
	y = tmp;
}

int main()
{
	int a = 10, b = 20;
	Swap(a, b); //使用下面专门处理int的Swap函数
	Swap<int>(a, b); //使用上面编译器特化后的Swap函数
	return 0;
}

        2、对于非模板函数和同名函数模板,调用时会调用最匹配的那个函数。在其他条件相同的情况下,编译器会优先调用非模板函数。如果模板可以产生一个更匹配的函数,则将选择模板。
 

//Add函数模板
template<class T1,class T2>
T1 Add(T1 x, T2 y)
{
	return x + y;
}

//处理int类型的Add函数
int Add(int x, int y)
{
	return x + y;
}

int main()
{
	Add(10, 20); //虽然可以通过模板生成<int,int>的模板函数,但编译器并不会去生成,而是去调用已经存在的非模板函数
	Add(10, 20.0); //通过函数模板生成的函数会更加匹配,故调用函数模版生成的Add函数
	return 0;
}

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

//Add函数模板
template<class T>
T Add(T x, T y)
{
	return x + y;
}

int Swap(int x, int y)
{
	int tmp = x;
	x = y;
	y = tmp;
}

int main()
{
	int a = 20;
	double d = 10.0;
	Add(d, a); //这里会报错,Add是模板函数,不支持自动类型转换
	Swap(d, a); //这里编译通过,d会发生隐式类型转换为int类型
	return 0;
}


四. 类模版

4.1 类模版的定义

        在C++中,除了普通函数支持模版,类也支持模版,我们把这样的类称为类模版。下面给出一个类模版的定义格式:

template<class T1, class T2, ..., class Tn> //模板参数
class 类模板名
{
	// 类内成员定义
};

        下面我们用vector类来演示一下类模板的定义方法以及注意事项(只是演示噢,并不是真正的vector模拟实现)

//vector类
template<class T>
class Vector
{
public:
    //构造时使用new动态申请空间
	Vector(int capacity = 10)
		:_p(new T[capacity])
		_capacity(capacity)
		,_size(0)
	{}

	void puch_back(const T& x); //尾插
	void pop_back(); //尾删

	~Vector(); //析构时需要调用delete释放空间
private:
	T* _p;
	int _capacity;
	int _size;
};

//在类模板外定义成员函数,需要加上模版参数列表
template<class T>
Vector<T>::~Vector()
{
	if (_p != nullptr)
	{
		delete[] _p;
		_capacity = _size = 0;
	}
}

值得注意的是:和模板函数一样,类模板并不是一个具体的类,它只是一个模具,只有进行实例化后才会变成一个具体的类。

4.2 类模版的实例化

        与函数模板实例化不同,类模板不支持隐式类型推导,类模板实例化需要在类模板名字后用<>显式指定模板参数的实际类型。注意:类模板的名字不是真正的类,只有指定类型实例化后才是真正的类。

//模板的实例化
int main()
{
    //Vector是类名,Vector<int>才是类型名,才能用来定义对象
	Vector<int> vi(20); //用参数为int的Vector类定义对象
	Vector<double> vd(10); //用参数为double的Vector类定义对象
	return 0;
}

        类模版经过类型实例化后得到的类我们称作模板类,通过模板类我们就可以定义许多相应的类对象,三者的关系可用下图表示: 


 以上,就是本期的全部内容啦🌸

制作不易,能否点个赞再走呢🙏

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

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

相关文章

DIY相机(一)libcamera库

相机选型 DIY相机首先是要确定使用的相机型号。兼容树莓派&#xff0c;画质好一些的&#xff0c;目前主要有两款&#xff1a;一是Raspberry Pi Camera Module 3&#xff0c;二是Raspberry Pi HQ Camera。 下图是Raspberry Pi Camera Module 3的相关特性。支持自动对焦和HDR等…

opencv dnn模块 示例(20) 目标检测 object_detection 之 yolor

文章目录 1、论文介绍1.1、YOLOR思想动机1.2、隐式知识学习1.2.1、隐式知识如何工作1.2.2、隐式知识统一网络建模 1.3、实验1.4、总结 2、测试2.1、opencv dnn2.1.1、代码2.1.2、结果 2.2、测试效率 YOLOR出自论文You Only Learn One Representation: Unified Network for Mult…

【mfc/VS2022】计图实验:绘图工具设计知识笔记3

实现类对串行化的支持 如果要用CArchive类保存对象的话&#xff0c;那么这个对象的类必须支持串行化。一个可串行化的类通常有一个Serialize成员函数。要想使一个类可串行化&#xff0c;要经历以下5个步骤&#xff1a; 1、从CObject派生类 2、重写Serialize成员函数 3、使用DE…

【计算机网络 】传输层——UDP

目录 传输层传输层概念再谈端口号协议号和端口号端口号区域常见端口号pidof UDPUDP协议格式UDP协议的特点UDP的缓冲区UDP传输数据注意事项 传输层 传输层概念 在学习HTTP等应用层协议时&#xff0c;为了便于理解&#xff0c;可以简单的认为HTTP协议是将请求和响应直接发送到了…

​​​​​​​为什么你的Word文件无法移动到U盘

为什么你的Word文件无法移动到U盘 你是否遇到过这样的情况&#xff1a;你在苹果电脑上编辑了一个Word文档&#xff0c;想要把它拷贝到一个U盘上&#xff0c;但是却发现无法操作。你可能会感到很奇怪&#xff0c;为什么你的Word文件无法移动到U盘呢&#xff1f;这是因为苹果电脑…

ESM蛋白质语言模型系列

模型总览 第一篇《Biological structure and function emerge from scaling unsupervised learning to 250 million protein sequences 》ESM-1b 第二篇《MSA Transformer》在ESM-1b的基础上作出改进&#xff0c;将模型的输入从单一蛋白质序列改为MSA矩阵&#xff0c;并在Tran…

TensorRT量化实战课YOLOv7量化:pytorch_quantization介绍

目录 前言1. 课程介绍2. pytorch_quantization2.1 initialize函数2.2 tensor_quant模块2.3 TensorQuantizer类2.4 QuantDescriptor类2.5 calib模块 总结 前言 手写 AI 推出的全新 TensorRT 模型量化实战课程&#xff0c;链接。记录下个人学习笔记&#xff0c;仅供自己参考。 该…

分类预测 | Matlab实现KOA-CNN-BiLSTM-selfAttention多特征分类预测(自注意力机制)

分类预测 | Matlab实现KOA-CNN-BiLSTM-selfAttention多特征分类预测&#xff08;自注意力机制&#xff09; 目录 分类预测 | Matlab实现KOA-CNN-BiLSTM-selfAttention多特征分类预测&#xff08;自注意力机制&#xff09;分类效果基本描述程序设计参考资料 分类效果 基本描述 1…

【MySQL--->内外连接】

文章目录 [TOC](文章目录) 一、内连接二、左外连接三、右外连接 一、内连接 内连接就是将两个表连接进行笛卡尔积查询 显示SMITH的名字和部门名称 二、左外连接 左外连接就是以左面的表为主&#xff0c;即便是右边的表没有而左边表项中有的&#xff0c;依然显示 查询所有学…

HTML基础总结——速通知识点

一、基础知识点 Web标准构成&#xff1a; HTML页面的固定结构 <html><head><title>网页的标题</title> </head> <body>网页的主体内容 </body> </html>二、语法 2.1注释 在vscode中&#xff1a;将光标置于需要注释的行&a…

引入个性化标签的协同过滤推荐算法研究_邢瑜航

第3章 引入个性化标签的I-CF推荐算法 3.2.2 相似性度量方法 3.2.3 改进后的算法步骤与流程

IntelliJ IDEA 把package包展开和压缩

想要展开就把对勾取消&#xff0c;想要压缩就勾上

【多线程面试题十二】、阻塞线程的方式有哪些?

文章底部有个人公众号&#xff1a;热爱技术的小郑。主要分享开发知识、学习资料、毕业设计指导等。有兴趣的可以关注一下。为何分享&#xff1f; 踩过的坑没必要让别人在再踩&#xff0c;自己复盘也能加深记忆。利己利人、所谓双赢。 面试官&#xff1a;阻塞线程的方式有哪些&a…

【LeetCode力扣】42. 接雨水

目录 1、题目介绍 2、解题思路 2.1、暴力破解法 2.2、双指针法 1、题目介绍 原题链接&#xff1a; 42. 接雨水 - 力扣&#xff08;LeetCode&#xff09; 示例 1&#xff1a; 输入&#xff1a;height [0,1,0,2,1,0,1,3,2,1,2,1]输出&#xff1a;6解释&#xff1a;上面是由…

Python打包成.exe文件直接运行

文章目录 前言pyinstaller.exe文件具体步骤第一步&#xff1a;安装pyinstaller第二步&#xff1a;进入要打包文件的目录第三步&#xff1a;执行文件第四步&#xff1a;发给好友 拓展尾声 前言 很多小伙伴在阅读了博主的文章后都积极与博主交流&#xff0c;在这里博主很感谢大家…

scrapy-redis分布式爬虫(分布式爬虫简述+分布式爬虫实战)

一、分布式爬虫简述 &#xff08;一&#xff09;分布式爬虫优势 1.充分利用多台机器的带宽速度 2.充分利用多台机器的ip地址 &#xff08;二&#xff09;Redis数据库 1.Redis是一个高性能的nosql数据库 2.Redis的所有操作都是原子性的 3.Redis的数据类型都是基于基本数据…

LED数码管的静态显示与动态显示(Keil+Proteus)

前言 就是今天看了一下书上的单片机实验&#xff0c;发现很多的器件在Proteus中都不知道怎么去查找&#xff0c;然后想做一下这个实验&#xff0c;尝试能不能实现&#xff0c;LED数码管的两个还可以实现&#xff0c;但是用LED点阵显示器的时候他那个网络标号不知道是什么情况&…

GZ035 5G组网与运维赛题第7套

2023年全国职业院校技能大赛 GZ035 5G组网与运维赛项&#xff08;高职组&#xff09; 赛题第7套 一、竞赛须知 1.竞赛内容分布 竞赛模块1--5G公共网络规划部署与开通&#xff08;35分&#xff09; 子任务1&#xff1a;5G公共网络部署与调试&#xff08;15分&#xff09; 子…

python自动化测试(六):唯品会商品搜索-练习

目录 一、配置代码 二、操作 2.1 输入框“运动鞋” 2.2 点击搜索按钮 2.3 选择品牌 2.4 选择主款 2.5 适用性别 2.6 选择尺码 2.7 选择商品&#xff1a;&#xff08;通过css的属性去匹配&#xff09; 2.8 点击配送地址选项框 一、配置代码 # codingutf-8 from selen…

基于萤火虫算法的无人机航迹规划-附代码

基于萤火虫算法的无人机航迹规划 文章目录 基于萤火虫算法的无人机航迹规划1.萤火虫搜索算法2.无人机飞行环境建模3.无人机航迹规划建模4.实验结果4.1地图创建4.2 航迹规划 5.参考文献6.Matlab代码 摘要&#xff1a;本文主要介绍利用萤火虫算法来优化无人机航迹规划。 1.萤火虫…