万能钥匙:解锁 C++ 模板的无限可能

news2024/9/20 22:47:06

1.泛型编程

1.1:交换两个数(C语言)

1.2:交换两个数(C++)

1.3:泛型编程

2:函数模板

2.1:函数模板的概念

2.2:函数模板的格式

​编辑

2.3:函数模板的原理

2.4:模板的实例化 

2.4.1:隐式实例化

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

2.4.2.1:代码1

2.4.2.2:代码2(强制类型转换)

2.4.2.3:代码3(显式实例化)

2.5:模板参数的匹配规则

2.5.1:代码1(有现成的,匹配现成的)

2.5.2:代码2(有现成的,但是不能够匹配,有模版的前提下,就会选择自己实例化模板)

3.:类模板

3.1:类模板的定义格式

3.2:类模板的实例化

3.2.1:代码1

3.2.2:代码2(类模板中函数放在类外进行定义时,需要加模板参数列表)


1.泛型编程

在C语言阶段,我们交换两个数通过使用指针来进行交换,在C++阶段,有了引用以后,我们可以通过使用引用来交换两个数.

1.1:交换两个数(C语言)

#define  _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;

void Swap(int *e1,int *e2)
{
	int temp = *e1;
	*e1 = *e2;
	*e2 = temp;
}

int main()
{
	int a = 0;
	int b = 0;
	scanf("%d %d", &a, &b);
	printf("交换前:a = %d, b = %d\n", a, b);
	Swap(&a, &b);
	printf("交换后:a = %d, b = %d\n", a, b);

	return 0;
}

1.2:交换两个数(C++)

#define  _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;

void Swap(int &e1,int &e2)
{
	int temp = e1;
	e1 = e2;
	e2 = temp;
}

int main()
{
	int a = 0;
	int b = 0;
	scanf("%d %d", &a, &b);
	printf("交换前:a = %d, b = %d\n", a, b);
	Swap(a,b);
	printf("交换后:a = %d, b = %d\n", a, b);

	return 0;
}

但是,有个通病,如果这样子的话,针对不同的数据类型,我们每次在进行交换的时候,需要分别单独造轮子,那么极其不方便.

1.3:泛型编程


void Swap(int & element1,int & element2)
{
	int temp = element1;
	element1 = element2;
	element2 = temp;
}

void Swap(double & element1,double & element2)
{
	double temp = element1;
	element1 = element2;
	element2 = temp;
}
void Swap(char & element1, char & element2)
{
	char temp = element1;
	element1 = element2;
	element2 = temp;
}
使用函数重载虽然可以实现,但是有几个不好的地方:
  • 1. 重载的函数仅仅是类型不同,代码复用率比较低,只要有新类型出现时,就需要用户自己增加对应的函数.
  • 2. 代码的可维护性比较低,一个出错可能所有的重载均出错.
能否告诉编译器一个模子,让编译器根据不同的类型利用该模子来生成代码 呢?
如果在 C++ 中,也能够存在这样一个 模具 ,通过给这个模具中 填充不同材料 ( 类型 ) 获得不同材料的铸件~
( 即生成具体类型的代码),那么会省力很多, 巧的是 前人早已将树栽好,我们只需要在此乘凉.

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

2:函数模板

2.1:函数模板的概念

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

2.2:函数模板的格式

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

返回值类型 函数名(参数列表){}

#define  _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;

/*
* typename是用来定义模板参数的关键字,也可以使用class
*/
template<typename T>
void Swap(T & element1,T & element2)
{
	T temp = element1;
	element1 = element2;
	element2 = temp;
}

int main()
{
	int a = 0;
	int b = 0;
	double c = 0.0;
	double d = 0.0;
	scanf("%d %d", &a, &b);
	printf("交换前:a = %d, b = %d\n", a, b);
	Swap(a,b);
	printf("交换后:a = %d, b = %d\n", a, b);
	scanf("%lf %lf", &c, &d);
	printf("交换前:c = %lf, d = %lf\n", c, d);
	Swap(c,d);
	printf("交换后:c = %lf, d = %lf\n", c, d);

	return 0;
}

2.3:函数模板的原理

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

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

 

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

2.4:模板的实例化 

编译通过推出类型,用函数模板,生成对应的函数,这个过程叫做模板实例化.

2.4.1:隐式实例化

概念:隐式实例化,由编译器根据实参来推演模板参数的实际类型.

#define  _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;

template <class T>

T Add(const T & element1,const T & element2)
{
	return element1 + element2;
}


int main()
{
	int a1 = 10, a2 = 20;
	double d1 = 10.1, d2 = 20.1;
	//隐式实例化,由编译器根据实参来推演模板参数的实际类型
	cout << Add(a1, a2) << endl;
	cout << Add(d1, d2) << endl;
	return 0;
}

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

2.4.2.1:代码1
#define  _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;

template <class T>

T Add(const T & element1,const T & element2)
{
	return element1 + element2;
}


int main()
{
	int a1 = 10, a2 = 20;
	double d1 = 10.1, d2 = 20.1;

	/*
	* 该语句不能通过编译,因为在编译期间,当编译器看到该实例化时,需要推演其实参类型
	通过实参a1将T推演为int,通过实参d1将T推演为double类型,但模板参数列表中只有一个T,编译器无法确定此处到底该将T确定为int 或者 double类型而报错
	注意:在模板中,编译器一般不会进行类型转换操作,因为一旦转化出问题,编译器就会当背锅侠了
	*/
	cout << Add(a1, d2);
	return 0;
}

 

该语句不能通过编译,因为在编译期间,当编译器看到该实例化时,需要推演其实参类型
通过实参a1将T推演为int,通过实参d1将T推演为double类型,但模板参数列表中只有一个T,编译器无法确定此处到底该将T确定为int 或者 double类型而报错~
PS:在模板中,编译器一般不会进行类型转换操作,因为一旦转化出问题,编译器就成了背锅侠~
2.4.2.2:代码2(强制类型转换)
#define  _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;

template <class T>
T Add(const T& element1, const T& element2)
{
	return element1 + element2;
}
int main()
{
	int a1 = 10, a2 = 20;
	double d1 = 10.1, d2 = 20.1;
	//方案一强制类型转换
	cout << Add(a1, (int)d2) << endl;
	return 0;
}

2.4.2.3:代码3(显式实例化)
在函数名后的<>中指定模板参数的实际类型.
PS:如果类型不匹配,编译器会尝试进行隐式类型转换,如果无法转换成功编译器将会报错
#define  _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;

template <class T>
T Add(const T& element1, const T& element2)
{
	return element1 + element2;
}
int main()
{
	int a1 = 10, a2 = 20;
	double d1 = 10.1, d2 = 20.1;
	//显式实例化
	cout << Add<int>(a1, d2) << endl;
	return 0;
}

2.5:模板参数的匹配规则

  • 1.一个非模板参数可以和一个同名的函数模板同时存在,而且该函数模板还可以被实例化为非模板函数即有现成的,匹配现成的~

  • 2.对于非模板函数与同名函数模板,如果其他条件都相同,在调动时会优先调用非模板函数而不是从该模板产生出一个实例.如果模板可以产生一个具有更好匹配的函数,那么将会选择模板即有现成的,但是不能够匹配,有模版的前提下,就会选择自己实例化模板~

2.5.1:代码1(有现成的,匹配现成的)

#define  _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;

template <class T>
T Add(T& element1,T& element2)
{
	return element1 + element2;
}

int Add(int & element1,int & element2)
{
	return element1 + element2;
}
int main()
{
	int a1 = 10, a2 = 20;
	double d1 = 10.1, d2 = 20.1;
	//有现成的,匹配现成的
	cout << Add(a1, a2) << endl;
	return 0;
}

2.5.2:代码2(有现成的,但是不能够匹配,有模版的前提下,就会选择自己实例化模板)

#define  _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;

template <class T>
T Add(T& element1,T& element2)
{
	return element1 + element2;
}

int Add(int & element1,int & element2)
{
	return element1 + element2;
}
int main()
{
	int a1 = 10, a2 = 20;
	double d1 = 10.1, d2 = 20.1;
	//有现成的,但是不能够匹配,有模版的前提下,就会选择自己实例化模板
	cout << Add(d1, d2) << endl;
	return 0;
}

3.:类模板

3.1:类模板的定义格式

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

3.2:类模板的实例化

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

3.2.1:代码1

#define  _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;

template <class T>

class Stack
{
public:
	void Push(const T & data)
	{
		///
	}
private:
	int* _Node;
	int  _top;
	int  _capacity;
};

int main()
{
	//同一个模板显示实例化出的两个不同类型
	Stack<int> st1;
	Stack<double> st2;

	return 0;
}

3.2.2:代码2(类模板中函数放在类外进行定义时,需要加模板参数列表)

#define  _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;

template <class T>

class Stack
{
public:
	//进行生命
	void Push(const T& data);
private:
	int* _Node;
	int  _top;
	int  _capacity;
};

/注意:类模板中函数放在类外进行定义时,需要加模板参数列表
template<class T>
void Stack<T>::Push(const T& data);

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

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

相关文章

Unidbg使用指南

Unidbg使用指南 简介使用Unidbg补环境仅含C语言C调用 Java 实操——车智赢在unidbg实现执行so中的方法附——关于引用数据类型的转换附——静态注册和动态注册模板静态注册动态注册 现在很多的app使用了so加密&#xff0c;以后会越来越多。爬虫工程师可能会直接逆向app&#xf…

黑马前端——days09_css

案例 1 页面框架文件 <!DOCTYPE html> <html lang"zh-CN"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><meta http-equiv"X-UA-Compati…

Ubuntu20.04如何安装配置JDK

资源准备 官方下载地址&#xff08;根据自己的系统版本选择不同版本进行下载即可&#xff09;&#xff1a;Java Downloads | Oracle 如无特殊需要可直接移步至下方JDK1.8安装包 https://download.csdn.net/download/qq_43439214/89646731 安装步骤 创建Java目录 sudo mkdir …

jmeter安装及环境变量配置、Jmeter目录介绍和界面详解

一 JMeter简介 Apache JMeter是100%纯JAVA桌面应用程序&#xff0c;被设计为用于测试客户端/服务端结构的软件(例如web应用程序)。它可以用来测试静态和动态资源的性能&#xff0c;例如&#xff1a;静态文件&#xff0c;Java Servlet,CGI Scripts,Java Object,数据库和FTP服务器…

【已解决】在进行模型量化推理的过程中遇到的错误以及解决方法

①在使用vLLM推理模型时&#xff0c;出现&#xff1a; Error in calling custom op rms_norm: _OpNamespace _C object has no attribute rms_norm 尝试众多解决方法之后&#xff0c;包括重新安装 pip install vllm0.5.0 对我有用的解决方法&#xff1a; 修改子目录下的vll…

【2024最新】Windows系统上NodeJS安装及环境配置图文教程

Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境&#xff0c;允许在服务器端运行 JavaScript 代码。它采用事件驱动、非阻塞 I/O 模型&#xff0c;非常适合构建高性能的网络应用程序。Node.js 提供了一系列内置模块&#xff0c;支持异步编程&#xff0c;易于扩展&…

机器学习:knn算法实现图像识别

1、概述 使用K-近邻&#xff08;K-Nearest Neighbors, KNN&#xff09;算法对手写数字进行识别的过程。通过读取一张包含多个手写数字的图片&#xff0c;将其分割成单独的数字图像&#xff0c;并将其作为训练和测试数据集。 2、数据处理思路 1、图像分割该数据有50行100列&am…

手机设备IP地址切换:方法、应用与注意事项

在当今数字化时代&#xff0c;手机已成为我们日常生活中不可或缺的一部分。无论是工作、学习还是娱乐&#xff0c;手机都扮演着重要角色。然而&#xff0c;随着网络环境的日益复杂&#xff0c;有时我们需要切换手机设备的IP地址以满足特定的需求&#xff0c;如保护隐私、绕过地…

算法笔记:空间填充曲线

空间填充曲线&#xff08;Space-filling curve&#xff09;是一种数学曲线&#xff0c;它可以无间断地覆盖一个多维空间的每一个点&#xff0c;从而实现从一维到多维的映射。用以解决连续与离散空间之间的映射问题。空间填充曲线的应用广泛&#xff0c;包括图像处理、地理信息系…

基于微信小程序的诗词智能学习系统的设计与实现(全网独一无二,24年最新定做)

文章目录 前言&#xff1a; 博主介绍&#xff1a; ✌我是阿龙&#xff0c;一名专注于Java技术领域的程序员&#xff0c;全网拥有10W粉丝。作为CSDN特邀作者、博客专家、新星计划导师&#xff0c;我在计算机毕业设计开发方面积累了丰富的经验。同时&#xff0c;我也是掘金、华为…

dos攻击漏洞思路小结

前言 想挖掘src拒绝服务类型的漏洞&#xff0c;搜索了一圈社区相关文章较少&#xff0c;这里根据自己的一些实战案例归纳思路来抛砖引玉&#xff0c;希望能对各位师傅有所帮助&#xff01; 从黑盒视角搭配实际场景&#xff0c;说明如何具体操作能够快速的挖掘拒绝服务漏洞。 …

vue3中使用useStore(),返回undefined的踩坑记录

vue3中使用useStore()&#xff0c;返回undefined&#xff0c;排查后&#xff0c;记录一下的踩坑记录。 总结为&#xff0c;三检查&#xff1a; 1、一检查版本 在package.json中检查&#xff0c;vuex是否正常引入&#xff1a; 版本也要确认一下&#xff1a; vue3对应vuex4的…

使用光流进行相机运动估计

文章目录 基本相机移动区分动作的核心思想了解代码参考 基本相机移动 从我的非专业角度来看&#xff0c;尽管已知的摄像机运动有多种&#xff0c;但我们应该概述其中三种&#xff1a; 一种是将摄像机安装在轨道上并移动——卡车、移动式摄影车、基座摄像机停留在同一位置并旋…

MySQL中的distinct和group by哪个效率更高?

前言 大家好&#xff0c;我是月夜枫~~ 一、distinct和group by的区别 1.1.作用方式和应用场景 ‌group by和‌distinct的主要区别在于它们的作用方式和应用场景。 group by用于对数据进行分组和聚合操作&#xff0c;通常与聚合函数&#xff08;如COUNT、SUM、AVG等&#xf…

学习分享:微软Edge浏览器全解析(请按需收藏)

成长路上不孤单&#x1f60a;【14后小学生一枚&#xff0c;C爱好者&#xff0c;持续分享所学&#xff0c;如有需要欢迎收藏转发&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;】 微软Edge浏览器是一款由微软开发的现代网页浏览…

Python(PyTorch)硅光电倍增管和量化感知训练亚光子算法验证

&#x1f3af;要点 &#x1f3af;亚光子光神经网络矩阵计算 | &#x1f3af;光学扇入计算向量点积 | &#x1f3af;表征测量确定不同光子数量下计算准确度 | &#x1f3af;训练全连接多层感知器基准测试光神经网络算法数字识别 | &#x1f3af;物理验证光学设备设置 | &#x…

【闭包】闭包知识点总结

一、什么是闭包&#xff1f; ——官方解释&#xff1a; 一个函数对周围状态的引用捆绑在一起&#xff0c;内层函数中访问到其外层函数的作用域 ——简单解释&#xff1a; &#x1f449; 闭包内层函数可以引用的外层函数的变量 ——闭包优势 可以保护内部变量&#xff0c;不让外…

黑马前端——days11_综合案例

文章目录 一、首页1、页面开头2、快捷导航2.1 页面框架2.2 格式文件 3、头部模块3.1 页面框架3.2 格式文件 4、导航栏4.1 页面框架4.2 格式文件 5、页面主模块5.1 页面框架5.2 格式文件 6、推荐模块6.1 页面框架6.2 格式文件 7、楼层模块7.1 页面框架7.2 格式文件 8、页面底部8…

webrtc学习笔记2

音视频采集和播放 打开摄像头并将画面显示到页面 1. 初始化button、video控件 2. 绑定“打开摄像头”响应事件onOpenCamera 3. 如果要打开摄像头则点击 “打开摄像头”按钮&#xff0c;以触发onOpenCamera事件的调用 4. 当触发onOpenCamera调用时 a. 设置约束条件&#xff0c…

虚幻UE5安装报错误代码:SU-PQR5

找到图标的快捷方式 “Epic Games Launcher”右键属性&#xff0c;在目标最后添加-SkipBuildPatchPrereq&#xff0c;如下图&#xff1a; 最后&#xff0c;见证奇迹成功打开软件&#xff0c;可以继续安装啦。 参考资料&#xff1a; 【图片】求教各位大佬--错误代码SU-PQR5【ep…