【C++】——模板(泛型编程+函数模板+类模板)

news2024/11/25 15:04:54

文章目录

  • 1. 前言
  • 2. 泛型编程
  • 3. 函数模板
    • 3.1 函数模板的原理
    • 3.2 函数模板的实例化
    • 3.3 模板参数的匹配原则
  • 4. 类模板
    • 4.1 类模板的实例化
  • 5. 结尾

1. 前言

之前我们学习了函数重载,让我们在写相似函数的时候非常方便,但函数重载还有很多不足的地方,比如,每次写相似函数的时候,都要我们重新重载一个逻辑、代码几乎一样的函数,这就导致了我们的效率变低,所以我们今天来学习C++模板的相关知识点,学习完模板之后,我们就可以很好的解决这些问题。

2. 泛型编程

以前我们要实现一个通用的交换函数时是怎么做的呢?我们利用了函数重载,把可能用到的函数都重载出来。

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++中,能够存在一个模具,通过给这个模具中填充不同材料(类型),来获得不同材料的铸件(即生成具体类型的代码)这样就会让我们大幅提高效率。

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

3. 函数模板

概念
函数模板代表了一个函数家族,该函数模板与类型无关,在使用时被参数化,根据实参类型产生函数的特定类型版本。
格式
template<typename T1, typename T2,…,typename Tn>
返回值类型 函数名(参数列表) {}
注意
typename是用来定义模板参数关键字,也可以使用class。
typename后面类型名字T是随便取的。

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

3.1 函数模板的原理

函数模板是一个蓝图,它本身并不是函数,是编译器用使用方式产生特定具体类型函数的模具。所以其实模板就是将本来应该我们做的重复的事情交给了编译器。
在编译器编译阶段,对于模板函数的使用,编译器需要根据传入的实参类型来推演生成对应类型的函数以供调用。
比如:当用double类型使用函数模板时,编译器通过对实参类型的推演,将T确定为double类型,然后产生一份专门处理double类型的代码,对于字符类型也是如此。

3.2 函数模板的实例化

用不同类型的参数使用函数模板时,称为函数模板的实例化。模板参数实例化分为:隐式实例化和显式实例化。
隐式实例化:让编译器根据实参推演模板参数的实际类型
显式实例化:在函数名后的<>中指定模板参数的实际类型

template<typename T>
T Add(const T& x, const T& y)
{
	return x + y;
}

int main()
{
	int a = 1, b = 2;
	double c = 1.1, d = 2.2;

	//编译器自动推演,隐式实例化
	cout << Add(a, b) << endl;
	cout << Add(c, d) << endl;

	//推演实例化矛盾,编译器无法确定此处到底该将T确定为int或者double类型而报错
	//cout << Add(a, d) << endl;

	//俩种处理方式:1、强制转换类型   2、显示实例化
	//1.强制转换类型
	cout << Add((double)a, d) << endl;
	cout << Add(a, (int)d) << endl;

	//2.显示实例化
	cout << Add<double>(a, d) << endl;
	cout << Add<int>(a, d) << endl;

	return 0;
}

在这里插入图片描述

3.3 模板参数的匹配原则

1.一个非模板函数可以和一个同名的函数模板同时存在,而且该函数模板还可以被实例化为这个非模板函数。
2.对于非模板函数和同名函数模板,如果其他条件都相同,在调动时会优先调用非模板函数而不会从该模板产生出一个实例。如果模板可以产生一个具有更好匹配的函数, 那么将选择模板。
3.模板函数不允许自动类型转换,但普通函数可以进行自动类型转换。

//专门处理加法的函数
int Add(const int& x, const int& y)
{
	return x + y;
}

//通用加法函数
template<class T>
T Add(const T& x, const T& y)
{
	return x + y;
}

template<class T1, class T2>
T2 Add(const T1& x, const T2& y)
{
	return x + y;
}

int main()
{
	cout << Add(1, 2) << endl;//调用int Add函数
	cout << Add(1.1, 2.2) << endl;//调用T Add函数
	cout << Add(1, 2.2) << endl;//调用T2 Add函数

	return 0;
}

4. 类模板

格式
template<class T1, class T2, …, class Tn>
class 类模板名
{
// 类内成员定义
};

template<class T>
class Stack
{
public:
	Stack(size_t capacity = 4)
		:_a(nullptr)
		,_top(0)
		,_capacity(0)
	{
		if (capacity > 0)
		{
			_a = new T[capacity];
			_capacity = capacity;
			_top = 0;
		}
	}

	~Stack()
	{
		delete[] _a;
		_a = nullptr;
		_top = _capacity = 0;
	}

	void Push(const T& x)
	{}
	//注意:类模板中函数放在类外进行定义时,需要加模板参数列表

private:
	T* _a;
	size_t _top;
	size_t _capacity;
};

注意:类模板中函数放在类外进行定义时,需要加模板参数列表

4.1 类模板的实例化

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

template<class T>
class Stack//这里Stack不是具体的类,是编译器根据被实例化的类型生成具体类的模具
{
public:
	Stack(size_t capacity = 4)
		:_a(nullptr)
		,_top(0)
		,_capacity(0)
	{
		if (capacity > 0)
		{
			_a = new T[capacity];
			_capacity = capacity;
			_top = 0;
		}
	}

	~Stack()
	{
		delete[] _a;
		_a = nullptr;
		_top = _capacity = 0;
	}

	void Push(const T& x)
	{}

private:
	T* _a;
	size_t _top;
	size_t _capacity;
};

int main()
{
	//类模板都是显示实例化
	//虽然他们用了一个类模板,但是Stack<int>,Stack<char>是两个类型
	Stack<int> st1;
	Stack<char> st2;
	
	//知道要存多少个数据,避免插入时扩容消耗
	Stack<int> st3(100);

	return 0;
}

注意
1.类模板必须显示实例化。
2.虽然上面两个对象用了同一个类模板,但是Stack<int>,Stack<char>是两个不同的类型。

5. 结尾

C++模板入门的相关知识点我们就学习完了,学了模板以后,在很多地方我们就可以用模板来实现,而不是重载函数,这能极大的提高效率和可维护性。
最后,感谢各位大佬的耐心阅读和支持,觉得本篇文章写的不错的朋友可以三连关注支持一波,如果有什么问题或者本文有错误的地方大家可以私信我,也可以在评论区留言讨论,再次感谢各位。

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

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

相关文章

车载基础软件——嵌入式系统时间特性分析

我是穿拖鞋的汉子&#xff0c;魔都中坚持长期主义的工程师。 老规矩&#xff0c;分享一段喜欢的文字&#xff0c;避免自己成为高知识低文化的工程师&#xff1a; 人们会在生活中不断攻击你。他们的主要武器是向你灌输对自己的怀疑&#xff1a;你的价值、你的能力、你的潜力。他…

AI人工智能逻辑回归的原理、优缺点、应用场景和实现方法

逻辑回归&#xff08;Logistic Regression&#xff09;是一种常见的机器学习算法&#xff0c;它被广泛应用于分类问题。在人工智能&#xff08;Artificial Intelligence&#xff0c;简称AI&#xff09;领域中&#xff0c;逻辑回归是一种简单而有效的算法&#xff0c;可以用于许…

GC 三色标记算法(Go Java版本)

一、前言 GC全称Garbage Collection&#xff0c;目前主流的垃圾回收算法有两类&#xff0c;分别是追踪式垃圾回收算法&#xff08;Tracing garbage collection&#xff09;和引用计数法&#xff08; Reference counting &#xff09;。 而三色标记法是属于追踪式垃圾回收算法…

github copilot chat申请,安装,及常见问题解决

申请 首先申请&#xff0c;并开通copilot, 地址为&#xff1a;https://github.com/features/copilot&#xff0c;copilot 一个月10美金&#xff0c;第一个月免费&#xff0c;支持国内的信用卡。 开通copilot之后&#xff0c;可以申请 copilot chat 的预览版功能&#xff0c;网…

5-高级SQL查询

PS&#xff1a;插入查询结果 把一张表查询出的结果&#xff0c;插入到另一张表中。要求两张表的列数和列类型要匹配。 前面讲的所有select规则在此处都适用~ 1.聚合查询 聚合查询&#xff1a;行和行之间进行运算。带表达式查询&#xff1a;列和列之间进行运算。 常⻅的聚合函…

MySQL——聚合函数

我们上一篇讲到了 SQL 单行函数。实际上 SQL 函数还有一类,叫做聚合(或聚集、分组)函数,它是对一组数据进行汇总的函数,输入的是一组数据的集合,输出的是单个值。接下来我们来看看什么是聚合函数及聚合函数的分类 1. 聚合函数介绍 1.1 什么是聚合函数? 聚合函数作用于…

redis 7.x 缓存双写一致性的解决方案

一 redis缓存双写一致性 1.1 保证redis一致性的原则 1.给缓存设置过期时间&#xff0c;定期清理缓存并写回&#xff0c;是保证最终一致性的解决方案。使用场景&#xff1a;在数据读多写少的情况下作为缓存来使用。 我们可以对已存入缓存的数据设置过期时间&#xff0c;所有…

Qt-学习 QJson协议解析

内容来源于哔站视频&#xff0c;仅仅作为自己的笔记记录&#xff0c;感兴趣的小伙伴去原作品大call(此处给作者三鞠躬&#xff09;&#xff0c;Json以及Json在Qt中的使用 【Qt/C/C】_哔哩哔哩_bilibili 目录 1、Json介绍&#xff1a; 1.1 Json的定义 1.2 Json的数据格式 1…

Java面试知识点(全)- Java并发- Java并发基础

Java面试知识点(全) 导航&#xff1a; https://nanxiang.blog.csdn.net/article/details/130640392 注&#xff1a;随时更新 多线程解决什么问题 CPU、内存、I/O 设备的速度是有极大差异的&#xff0c;为了合理利用 CPU 的高性能&#xff0c;平衡这三者的速度差异&#xff0c…

thinkphp6中使用session设置无效问题的解决及注意事项

就如thinkphp官方在文档开头所描述的一样&#xff0c;ThinkPHP6.0基于精简核心和统一用法两大原则在5.1的基础上对底层架构做了进一步的优化改进&#xff0c;并更加规范化&#xff1b;其实从5.x版本开始thinkphp本身就是为api应用而生。特别是在6.0版本开始框架默认压根不开启和…

大数据-玩转数据-FLINK(Yarn模式)的安装与部署

一、为什么要用Flink on Yarn HA 模式 默认情况下&#xff0c;Flink 只有一个JobManager&#xff0c;这将导致单点故障&#xff0c;使用JobManager HA &#xff0c;集群可以从单点故障中恢复&#xff0c;从而避免单点故障&#xff0c;我们可以在Standalone 或 Flink on Yarn 集…

【2023 · CANN训练营第一季】初识新一代开发者套件 Atlas 200I DK A2---章节小测微认证考试

微认证考试 1、以下不属于模型适配工具内置的模型的是&#xff1f; A. 目标检测模型 B. 图像分类模型 C. 语义分割模型 D. 关键点检测模型 C 2、模型适配工具安装前需要先安装&#xff1f; A. mobaXterm B. python3.9 C. Vscode D. Anaconda D 3、在哪个目录下可查看端…

docker 部署 -v指令遇到docker: invalid reference format.

检查了半天才发现&#xff0c;换行符后面不能有空格&#xff01;有空格就报错&#xff01;

vite创建vue3项目

vite创建vue3项目 方式一&#xff08;推荐&#xff09; 脚手架快速创建&#xff0c;勾选依赖的方式 这种方式创建的项目最快捷&#xff0c;因为基本依赖都装好了 npm init vuelatest方式二 使用官方模板创建vue3项目 这种方式会基于模板创建项目&#xff0c;对于官方模板vu…

Rollup 基本概念及使用

Rollup是一款基于ESModule模块规范实现的JavaScript打包工具&#xff0c;在前端社区中赫赫有名&#xff0c;同时也在Vite的架构体系中发挥着重要作用。不仅是Vite生产环境下的打包工具&#xff0c;其插件机制也被Vite所兼容&#xff0c;可以说是Vite的构建基石。 接下来&#…

cesium entity与时间轴关联(添加运动轨迹)

1、先添加一个entity实体 用model加载一个飞机实体 let entity viewer.entities.add({position: Cesium.Cartesian3.fromDegrees(120, 21),model: {uri: "/static/Cesium_Air.glb",minimumPixelSize: 128,maximumScale: 20000,},}) viewer.trackedEntity entity;…

LIN-报文结构

文章目录 协议规范一、字节场二、报文头&#xff08;HEADER FIELDS&#xff09;同步间隔&#xff08;synchronisation break)同步场&#xff08;SYNCH FIELD&#xff09;标识符场&#xff08;IDENTIFIER FIELD&#xff09; 三、数据场&#xff08;DATE FIELDS&#xff09;四、校…

【负载均衡式在线OJ】 数据库

文章目录 41.使用Postman进行综合调试42.了解-前端预备52. 添加oj用户到MySQL53. 使用MySQL_Workbench创建表结构54. 测试录题功能55.重新设计oj_model56.编写oj_model具体代码57.MySQL综合测试58.结项与项目扩展思路 41.使用Postman进行综合调试 完善判题功能 先编译再测试 …

SpingBoot简介—helloworld

目录 一、使用idea创建第一个SpringBoot项目 二、Hello World 三、整体项目结构 一、使用idea创建第一个SpringBoot项目 在进行这一步时&#xff0c;可能会遇到问题&#xff0c;如下图所示&#xff1a; 在这里解决方法有以下四种&#xff1a; &#xff08;1&#xff09;方法1&…

国内AI大模型 VS chatgpt (20)-- H265/H264有何不同,h265有什么优势?

H265/H264有何不同&#xff0c;h265有什么优势? 文心一言&#xff1a; H.264和H.265是两种不同的视频编码标准&#xff0c;它们在压缩质量和带宽需求方面有所不同。 H.264是一种高效的视频压缩标准&#xff0c;它的编码单位是宏块&#xff08;macroblock&#xff09;&#…