C++ 初始模板

模板

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;
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

第四届“中国法研杯”司法人工智能挑战赛-刑期预测赛道三等奖方案

一、前言 本文将回顾第四届“中国法研杯”司法人工智能挑战赛-刑期预测算法赛道比赛。使用多任务预训练、然后进行微调的形式最终在比赛中取得了三等奖的成绩。 二、任务介绍 主办方在第一届“中国法研杯”比赛上提出了刑期预测任务&#xff0c;本届将针对往届刑期预测准确率…

(学习日记)AD学习 #1

写在前面&#xff1a; 由于时间的不足与学习的碎片化&#xff0c;写博客变得有些奢侈。 但是对于记录学习&#xff08;忘了以后能快速复习&#xff09;的渴望一天天变得强烈。 既然如此 不如以天为单位&#xff0c;以时间为顺序&#xff0c;仅仅将博客当做一个知识学习的目录&a…

Leetcode452. 用最少数量的箭引爆气球

Every day a Leetcode 题目来源&#xff1a;452. 用最少数量的箭引爆气球 解法1&#xff1a;排序 贪心 题解&#xff1a;用最少数量的箭引爆气球 我们首先随机地射出一支箭&#xff0c;再看一看是否能够调整这支箭地射出位置&#xff0c;使得我们可以引爆更多数目的气球。…

既然有了IP地址,为什么还需要MAC地址?两者到底有啥区别,深入分析后终于明白了!

在计算机网络中&#xff0c;IP地址和MAC地址是两个最基本的概念。IP地址在互联网中是用于标识主机的逻辑地址&#xff0c;而MAC地址则是用于标识网卡的物理地址。虽然它们都是用于标识一个设备的地址&#xff0c;但是它们的作用和使用场景是不同的。 IP地址是在网络层&#xff…

logstash同步数据从kafka到es集群

背景&#xff1a;需求是这样的&#xff0c;原始文件是txt文件&#xff08;每天300个文件&#xff09;&#xff0c;最终想要的结果是每天将txt中的数据加载到es中&#xff0c;开始的想法是通过logstash加载数据到es中&#xff0c;但是对logstash不太熟悉&#xff0c;不知道怎么讲…

基于SpringBoot的生鲜管理系统的设计与实现

背景 困扰交易市场的许多问题当中,生鲜交易管理一定是交易市场不敢忽视的一块。但是管理好生鲜交易又面临很多麻烦需要解决,例如有几个方面:第一,生鲜市场往往人数都比较多,如何保证能够管理到每一个商家,如何在工作琐碎,记录繁多的情况下将生鲜交易的当前情况反应给领导相关部…

柔顺机构学读书笔记1:悬臂梁变形

题目&#xff1a; 如图考虑悬臂梁&#xff0c;材料各向同性&#xff0c;即各个方向上的弹性模量和强度都相同。如果在x方向上作用一个可使最大应力等于屈服强度 S S S的力 F x F_x Fx​时&#xff0c; x x x轴方向的变形为多少&#xff0c;书上给出了答案&#xff1a; 我们来验…

2022级云曦实验室考试(一)pwn

讲真&#xff0c;俺都不知道pwn是啥&#xff0c;等俺搜搜&#xff01; pwn简介&#xff1a; CTF中的pwn指的是通过通过程序本身的漏洞&#xff0c;编写利用脚本破解程序拿到主机的权限&#xff0c;这就需要对程序进行分析&#xff0c;了解操作系统的特性和相关漏洞&#xff0…

SHELL——流程控制条件判断

1、判断当前磁盘剩余空间是否有20G&#xff0c;如果小于20G&#xff0c;则将报警邮件发送给管理员&#xff0c;每天检查一次磁盘剩余空间。 2、判断web服务是否运行 1&#xff09;、查看进程的方式判断该程序是否运行 2&#xff09;、通过查看端口的方式判断该程序是否运行&am…

数据分析真的很火吗?真的有很多企业需要这样的岗位吗?求大佬指点。

“我是去年毕业的&#xff0c;因为疫情影响&#xff0c;整个就业环境都很不好&#xff0c;很多企业都裁员了。加上疫情三年基本都是玩过去&#xff0c;也没啥一技之长&#xff0c;就业就更难了。听说现在做数据分析的人很多&#xff0c;我身边的朋友都在转行做数据分析。 其实…

【C++】哈希——unordered系列容器哈希概念哈希冲突

文章目录 1. unordered系列的关联式容器1.1 引言1.2 unordered_map的使用说明1.3 unordered_set的使用说明1.4 unordered_set和unordered_map的应用1.5 性能比较 2. 哈希概念3. 哈希函数4. 哈希冲突5. 哈希冲突的解决——开散列和闭散列5.1 闭散列5.2 开散列 1. unordered系列的…

Elasticsearch:Explicit mapping - 显式映射

显式映射相比较动态映射&#xff08;Dynamic mapping&#xff09;是需要我们在索引创建时就定义字段及其类型。这个和我们传统的 RDMS 数据库一样&#xff0c;在我们写入数据到数据库之前&#xff0c;我们需要工整地定义好每个字段及其类型和长度。Elasticsearch 既可以使用显式…

使用柔性数组重写MyString

hello&#xff0c;各位宝子&#xff0c;今天阿崽将使用c和柔性数组的方式重新去写String类 在开始本次知识前&#xff0c;首先给大家介绍下柔性数组这个buff特点&#xff1a; 结构中的柔性数组成员前面至少要包含一个其他成员 sizeof返回的这种结构大小不包括柔性数组的内存 …

数据结构课程设计——哈夫曼编/译码器

数据结构课程设计任务书 学生姓名&#xff1a; 专业班级&#xff1a;软件工程 指导教师&#xff1a; 工作单位&#xff1a; 题 目: 哈夫曼编/译码器 基础要求&#xff1a; &#xff08;1&#xff09;熟悉各种…

数字信号处理基础(二):FFT和IFFT的使用以及详细分析代码书写思路

目录 1. fft和ifft的原理1.1 fft1.2 ifft 2. 书写代码思路3. 完整代码4. 结果图 1. fft和ifft的原理 1.1 fft fft是快速傅里叶变换&#xff0c;是MATLAB中计算信号频谱的函数&#xff0c;使用方法是fft(x)&#xff0c;直接对信号x进行fft计算。 由于fft函数计算信号的频谱是0…

vue3与vue2共存环境搭建

1、全局安装vue2 npm install vue-cli -g2、自行在任意位置创建一个文件夹&#xff0c;局部安装vue3 npm初始化 npm initnpm初始化 提示&#xff1a; 初始化后 出现文件package.json 如果没有初始化 会报错&#xff0c;且文件夹中不会新增内容 3、局部安装vue3 npm install …

宏工科技“全面”发力CIBF,助推电池智造“高效提质”

5月16-18日&#xff0c;第十五届中国国际电池技术展览会&#xff08;CIBF2023&#xff09;在深圳盛大举行。宏工科技携电池材料与电池匀浆领域的创新产品和系统解决方案精彩亮相。 据了解&#xff0c;宏工科技在新能源行业的业务涉及电池材料整线产线、电池匀浆、电池回收三个…

R语言实践——rWCVP入门

rWCVP入门 介绍1. 访问到WCVP1.1 方法一1.2 方法二&#xff08;谨慎&#xff09; 2. WCVP数据筛选2.1 关于按分类单元筛选的说明2.2 关于按分布区域筛选的说明 笔者实践 介绍 世界维管植物名录&#xff08;WCVP&#xff09;是维管植物物种的全球共识。它提供了科学已知的> …

【C语言】结构体指针

结构体指针 结构体基础知识注意对于成员的赋值 结构体指针指向结构体变量的指针结构体指针与结构体成员指针用结构体指针引用结构体成员 结构体 基础知识 初识结构体&#xff0c;可以先看这篇浅显易懂的文章结构体–基础篇 所谓结构体&#xff0c;是一组类型可以不同的相关变…

怎么把录音转文字?推荐你这三款工具

随着科技不断发展&#xff0c;录音转文字的技术也逐渐被广泛应用于各种场景中。其中最常见的一种就是会议记录。在日常工作中&#xff0c;会议是企业和组织中必不可少的一个环节&#xff0c;但在会议过程中的录音和记录往往需要花费大量的时间和精力。这个时候&#xff0c;我们…