C++ 初始模板

news2024/11/29 4:38:37

模板

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)
{
	char tmp = *x;
	*x = *y;
	*y = tmp;
}

如上述所示,我们在实现函数的时候,有很多函数会像上述一样,实现过程差不多,但是由些许不同,那么我们都把这些实现出来会显得很呆,C++祖师爷也想到了,所以设计了模板。

函数模板

 像上述的三个函数,算法相同,只是 参数的类型不同,那么我们就可以定义 函数模板,这样不管是什么类型,都可以使用这个函数模板,来实现相同的 算法。

语法:

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

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

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

其实就是在模板出来的早期用的就是 clsss ,但是后面觉得不好区分就重写定义了 typename 这个关键字。

像上述交换变量的 函数我们就不用写 三个了,写一个就行了:

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

我们不管使用内置类型还是自定义类型都可以实现上述的 算法:

 而且,模板函数并不是调用的 这个模板,而是 编译器帮我们实现了 这些重载函数,然后去调用这些重载函数:

 我们发现上述 两个 Swap 函数的地址不同。

 编译器会根据我们 传入的参数来推演,推演出参数的类型:

 编译器推导出之后,就会自己实现这个模板当前对应类型的 实例化函数。

 这里其实就是 模板的 实例化,和对象的实例化有点类似。

 而且,这里推导的类型,可以是任意类型,不管是内置类型 还是 自定义类型都可以推导出来,也就是说不管是什么类型都可以使用 模板推导出来。

像上述这样使用 模板来实现的 编程,也被称为是 泛型编程,因为这里的 函数并不是针对某一种 类型,而是针对 很多的类型。

 模板的  " <> "  当中其实和 函数的参数列表差不多,我们可以指定多个参数类型,同时 模板当中的参数也可以是 函数的返回值类型:

 对于 template 函数模板的作用范围就是这个函数。

 模板函数的实例化

 模板函数的实例化分为两种:

隐式实例化,这是编译器自己去推导出来的实例化函数,但是有的时候,对应的模板参数并不能实现 我们想要的参数类型,如下例子:

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;
	cout << Add(a1, a2) << endl;
	cout << Add(d1, d2) << endl;

    cout << Add(a1 , d1 ) << endl; // 不能编译通过

	return 0;
}

我们在模板当中只定义了 一个 T 的模板参数,但是我们传入的参数确是 int 和 double 类型的,这样做编译器就很为难,他不知道应该 使用哪一个 类型作为推导出来的类型,报错:
 

 当我们遇到这种问题的时候,有两种解决方式,第一种就是进行强制类型转换,像上述例子就是把其中一个参数 强转成 另一个参数的类型:

	cout << Add(a1, (int)d1) << endl;
	cout << Add((double)a1, d1) << endl;

  而且向上述 使用 强制转换的方式 实现传参的时候要注意,上述是 传入的是引用:因为强制转换会产生临时变量,像上述的强制转换的 变量传参,不是直接传这个 d1 或者是 a1 这个变量引用给函数形参,而是把强制类型转换所产生的临时变量 赋值传给 函数形参,而 临时变量具有常性,所以,如果我们在函数形参位置不加 const 修饰的话,就会发生权限的放大 ,就会报错:

第二种,就是我们模板实例化的 第二种形式:显式实例化。

	cout << Add<int>(a1, d1) << endl;
	cout << Add<double>(a1, d1) << endl;

这个时候就不需要 编译器自己去进行参数类型的推演了,向上述代码,第一种就直接实例化为了 int ,第二种就是直接实例化为了 double。

显式实例化的 使用的场景最多是如下的 这种形式:

 有些函数不能自动推导 参数类型,只能使用显示实例化。

template<typename T>
T* Carray(int n)
{
	return new T[n];
}

int main()
{
	Carray(10);

	return 0;
}

 编译器进行推导的时候,必须通过参数进行推导,返回值,或者是函数其中的 算法来推导等等的这些方式都是不行的。

所以这里就要使用 显示实例化:

	Carray<double>(10);

通过显示实例化,在指定 模板当中的参数的类型,这样就可以实现上述函数的调用。

类模板

 C++当中想到,在类当中也有类似的,和上述函数算法相同,但是参数类型不同的问题,所以在C++ 当中 有类模板来解决上述问题。

语法:

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

我们在C当中实现 栈的时候,是把存储的数据类型 用 typedef 来重新命名,这样做的好处是以后我们想要栈存储其他类型的 数据的时候,只需要修改 typedef 这一行代码就行了,但是这样做只能修改一个 栈的实现。当我现在想要同时实现多个 存储不同数据类型的栈的时候,typedef 就不能实现了。

像上述功能,在C++当中使用 类模板就可以很方便的实现:

template<class T>
class Stack
{
public:
	Stack(size_t a = 3)
	{
		cout << "Stack(size_t a = 1)" << endl;

		_array = new T[capacity];
		if (_array == NULL)
		{
			perror("malloc fail");
			return;
		}

		_capacity = a;
		_size = 0;

	}

	~Stack()
	{
		cout << "~Stack()" << endl;

		if (_array)
		{
			free(_array);
			_array = NULL;
			_capacity = 0;
			_size = 0;
		}
	}

protected:
	T* _array;
	int         _size;
	int         _capacity;
};

那么对于上述实现的类模板,我们就可以这样来使用 这些类了:

int main()
{
	Stack<int> s1;
	Stack<char> s1;
	Stack<double> s1;

	return 0;
}

因为我们上述类的构造函数,没有使用 T 这个模板参数,所以,构造函数不一定都要 使用 类模板当中 参数,其实构造函数就不一定需要 使用 推演,像上述的初始化方式函数很香的。

 那么类模板当中也可以传入多个参数,如果我们有需求的话。

 对于 template 类模板的作用范围就是这个类。

 如果类模板当中的成员函数在类外面 定义的话,和普通类的 类外定义有些许不一样。

  •  首先,因为 对于 template 类模板的作用范围就是这个类  那么当我们类外定义的时候,编译器就不认识函数当中的 T 这个模板参数了。所以我们在类外进行 定义成员函数的时候,需要给这个函数重新声明这个 T 模板参数。
  • 其次,对于普通类,类名就是类型,我们在调用普通类的时候,都是直接使用 类名来作为这个 对象的 类的类型的;但是 模板类的类名不是类型,类模板的类型是 类名<模板类型> 这样的形式。所以,我们在 用 " :: " 来指明 这个函数是哪一个 域当中函数的时候,需要使用 类名<模板类型>  这样的方式来表明这个函数的域。

 那么根据上述两点,我们在类模板外声明 其中的成员函数的时候,应该这样来实现:

template <class T>
Stack<T>::Stack()
{
	cout << "Stack(size_t a = 1)" << endl;

	_array = new T[capacity];
	if (_array == NULL)
	{
		perror("malloc fail");
		return;
	}

	_capacity = a;
	_size = 0;
}

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

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

相关文章

【C++ 入坑指南】(09)数组

文章目录 简介一维数组1. 定义2. 特点3. 用途4. 示例 二维数组1. 定义2. 用途3. 示例 简介 C 支持数组数据结构&#xff0c;它可以存储一个固定大小的相同类型元素的顺序集合。数组是用来存储一系列数据&#xff0c;但它往往被认为是一系列相同类型的变量。 一维数组 1. 定义…

外企还是香啊~

小伙伴们大家好&#xff0c;我是阿秀。 三月份的时候我看了下外企&#xff0c;查了一些资料&#xff0c;最后查下来远远比我想的要多&#xff0c;可能很多人跟我一样&#xff0c;对外企的印象都停留在微软、谷歌、intel这些比较市值大的公司上。 其实远远不止&#xff0c;广义上…

密码学安全性证明(一)Cramer-Shoup密码系统

Cramer-Shoup密码系统来自于A Practical Public Key CryptosystemProvably Secure against Adaptive ChosenCiphertext Attack这篇论文 CDH问题回顾&#xff1a; 已知(g,g^x, gk)能否计算gxk DDH问题回顾&#xff1a; 已知(g,g^x, g^k &#xff0c;D)能否判断D是否等于g^xk 注意…

港科夜闻|香港科技大学赛马会研究院两位成员入选美国国家科学院

关注并星标 每周阅读港科夜闻 建立新视野 开启新思维 1、香港科技大学赛马会研究院两位成员入选美国国家科学院。香港科技大学赛马会研究院Gunther Uhlmann教授和香港科技大学客座教授戴碧瓘教授因在原创性研究方面的杰出和可持续的成就&#xff0c;入选美国国家科学院(NAS)。G…

API接口的工作原理以及可以帮我们实现什么功能?

一、API接口的工作原理 API接口是应用程序编程接口(Application Programming Interface)的缩写&#xff0c;是不同软件系统之间进行通信的一种方式。 API接口的工作原理是&#xff0c;通过预定义的接口规范&#xff0c;软件系统可以调用或提供API接口的服务&#xff0c;来实现…

【开源项目】权限框架Nepxion Permission原理解析

项目介绍 Nepxion Permission是一款基于Spring Cloud的微服务API权限框架&#xff0c;并通过Redis分布式缓存进行权限缓存。它采用Nepxion Matrix AOP框架进行切面实现&#xff0c;支持注解调用方式&#xff0c;也支持Rest调用方式 项目地址 https://toscode.gitee.com/nepxion…

实现图形算法API[软光栅渲染器,C++]

最近有点烦&#xff0c;发烧感冒了三天[事实上是俩天&#xff0c;第三天是因为摆得太舒服了索性多玩一天]&#xff0c;啥都没学&#xff0c;打守望先锋也把把被虐...&#xff0c;想着今天来提起键盘把之前的东西都总结一下。 那么话归真题&#xff0c;首先我是仿造opengl来写的…

tensorflow/keras如何自定义layer

大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。喜欢通过博客创作的方式对所学的…

【Linux高级 I/O(3)】如何使用阻塞 I/O 与非阻塞 I/O?——poll()函数

poll()函数介绍 系统调用 poll()与 select()函数很相似&#xff0c;但函数接口有所不同。在 select()函数中&#xff0c;我们提供三个 fd_set 集合&#xff0c;在每个集合中添加我们关心的文件描述符&#xff1b;而在 poll()函数中&#xff0c;则需要构造一个 struct pollfd 类…

opencv图像增强实现方法

opencv是一款开源的图像增强工具&#xff0c;主要用于在 python环境下实现图像增强功能。 使用 opencv实现图像增强&#xff0c;需要使用 opencv的 GUI模块&#xff0c;如图1所示。 在 opencv中&#xff0c;有一个 datasets模块&#xff0c;这个模块主要用于处理数据和可视化操…

剑指 Offer 30. 包含min函数的栈【辅助栈】

剑指 Offer 30. 包含min函数的栈【辅助栈】 文章目录 剑指 Offer 30. 包含min函数的栈【辅助栈】题目描述题解 题目描述 题解 class MinStack {/*** initialize your data structure here.*/Stack<Integer> A, B;public MinStack(){A new Stack<Integer>();B ne…

使用OpenCV进行肺炎诊断检测

肺炎是一种由感染引起的严重呼吸道疾病&#xff0c;特别是在高危人群中&#xff0c;可能会出现危及生命的并发症。必须尽快诊断和治疗肺炎&#xff0c;以最大限度地提高患者康复的机会。 诊断过程并不容易&#xff0c;需要一些医学实验室工具和先进的医疗技能&#xff0c;但我们…

计算机专业毕业,有人Offer 50w,有人挂科重修!

昨天有两个VIP的小伙伴问我问题&#xff1a; 同学小明&#xff1a;孟哥&#xff0c;我小硕一枚&#xff0c;有两个offer&#xff0c;一个拿到了阿里的offer&#xff0c;乱七八糟加起来有四五十&#xff1b;还有一个是老家的电网。但是父母想让我回去&#xff0c;毕竟稳定&#…

Kali-linux绕过Utilman登录

Utilman是Windows辅助工具管理器。该程序是存放在Windows系统文件中最重要的文件&#xff0c;通常情况下是在安装系统过程中自动创建的&#xff0c;对于系统正常运行来说至关重要。在Windows下&#xff0c;使用WindowsU组合键可以调用Utilman进程。本节将介绍绕过Utilman程序登…

瑞芯微RGMII的配置

主要配置项 除去复位等信号&#xff0c;我们主要关注两大块的配置&#xff1a; 时钟配置 MAC 采用125M时钟&#xff0c;PHY采用25M时钟。 主要配置时钟源&#xff0c;这个和具体硬件连线强相关。例如125M时钟可以来源于soc内部的PLL&#xff0c;也可以由对端PHY 提供。 由对端P…

【JS】1688- 重学 JavaScript API - Fetch API

❝ 前期回顾&#xff1a; 1. Page Visibility API 2. Broadcast Channel API 3. Beacon API 4. Resize Observer API 5. Clipboard API ❞ &#x1f3dd; 1. 什么是 Fetch API 1.1 概念介绍 Fetch API[1] 是一种现代的 JavaScript API&#xff0c;用于进行「网络请求」。它提供…

过滤器(filter)、watch 侦听器 、计算属性 、axios、vue-cli 的使用 、vue组件化

过滤器&#xff08;filter&#xff09;、watch 侦听器 、计算属性 、axios、vue-cli 的使用 、vue组件化 1.过滤器&#xff08;filter&#xff09;过滤器的注意点定义全局变量 2.watch 侦听器侦听器的格式 3.计算属性4.axiosaxios 的基本使用 5.vue-cli 的使用6.vue组件化 1.过…

安卓与串口通信-基础篇

前言 安卓并不仅仅只是一个手机操作系统&#xff0c;在很多领域都能见到安卓的身影。 无论是车载系统、工控系统、屏控系统还是物联网设备基本都有安卓的一席之地。 在所谓的寒冬之下&#xff0c;纯粹的安卓开发似乎已经不再吃香&#xff0c;于是越来越多的安卓开发者转向了…

浅谈新兴室内外无线局域精准定位技术UWB(超宽带)

浅谈新兴室内外无线局域精准定位技术UWB&#xff08;超宽带&#xff09; 1、UWB高精度定位系统概述2、与传统定位比较3、应用场景4、实现uwb高精度定位4、UWB室内定位的缺陷5、应用案例 1、UWB高精度定位系统概述 UWB室内定位技术是一种全新的、与传统通信技术有极大差异的通信…

【MySQL学习】事务管理

文章目录 一、事务的基本认识1.1 事务的基本概念1.2 事务的基本属性1.3 支持事务的存储引擎 二、为什么要有事务三、事务的基本操作3.1 事务的提交方式3.2 事务的操作案例 四、事务的隔离级别4.1 对事务隔离性的初步理解4.2 四种隔离级别4.3 读未提交&#xff08;Read Uncommit…