C++ 初了解模板

news2024/11/16 7:42:59

一.   泛型编程

我们若是想实现一个需要对各类数据通用的功能,在C语言中是不太现实的,而在C++中,我们可以运用函数重载,但我们依然需要写出多个内容极其类似的函数,例如想要实现交换

void Swap(int& a, int& b)
{
	int tmp = a;
	a = b;
	b = tmp;
}

void Swap(char& a, char& b)
{
	char tmp = a;
	a = b;
	b = tmp;
}

void Swap(double& a, double& b)
{
	double tmp = a;
	a = b;
	b = tmp;
}

void Swap(int*& a, int*& b)
{
	int* tmp = a;
	a = b;
	b = tmp;
}

想要实现通用,远远不止这些函数,而这些代码的复用性很低,同时,当一个出错可能全部会出现问题,函数的可维护性差。

有没有一种方式能够让我们只写一遍函数体就能实现各类数据的交换呢,就像是写一个板子来让编译器生成对应的函数

因此,我们就需要进行泛型编程

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

而模板又分为函数模板和类模板



二.   函数模板的原理

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

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

这一点,我们会在将下面函数模板时进行验证


三.   函数模板

1.概念

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


2.格式

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

//typename是用来定义模板参数关键字,也可以使用class
返回值类型 函数名(参数列表){}

 应用到上面我们所说的交换中

template <class T>
void Swap(T& a, T& b)
{
	T tmp = a;
	a = b;
	b = tmp;
}


3.实例化

用不同类型的参数使用函数模板时,称为函数模板的实例化。模板参数实例化分为:隐式实例化和显式实例化。

1. 隐式实例化

隐式实例化:让编译器根据实参推演模板参数的实际类型

还是上面的交换模板

我们只需要正常依照平常函数的方式就可以了

template <class T>
void Swap(T& a, T& b)
{
	T tmp = a;
	a = b;
	b = tmp;
}

int main()
{
	int a = 10,b = 20;
	double c = 10.1, d = 20.2;
	char e = 'e', f = 'f';
	Swap(a, b);
	Swap(c, d);
	Swap(e, f);
	cout << "a=" << a << "," << "b=" << b << endl;
	cout << "c=" << c << "," << "d=" << d << endl;
	cout << "e=" << e << "," << "f=" << f << endl;
	return 0;
}

可以看到,的确可以完成交换

 

而转到反汇编,可以看到,的确是调用的不同的函数

而毕竟是让编译器推演实际类型,很容易出现问题

例如我们想要使用一个求和的模板

T Add(const T& a, const T& b)
{
	T ret = a + b;
	return ret;
}

我们难免会遇到不同类型相加的情况,直接使用隐式实例化会出现一些问题

我们或许可以写作这样

template <class T1,class T2>

T2 Add(const T1& a, const T2& b)
{
	T2 ret = a + b;
	return ret;
}

int main()
{
	int a = 10;
	double b = 20.2;
	cout << Add(a, b) << endl;
	return 0;
}

但这种写法并不通用,例如我们传参时先传b再传a,那么我们的返回类型便是int,无法实现想要的功能。 

而为了真正解决这个问题,我们可以将实参在传参前进行强制类型转换

int main()
{
	int a = 10;
	double b = 20.2;
	cout << Add((double)a, b) << endl;
	return 0;
}

  而另一种方法,便是使用显式实例化

2.显式实例化

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

例如上面的问题,若是使用显式实例化

int main()
{
	int a = 10;
	double b = 20.2;
	cout << Add<double>(a, b) << endl;
	return 0;
}

如果类型不匹配,编译器会尝试进行隐式类型转换,如果无法转换成功编译器将会报错。

例如,我们若是这样写便会报错

 


 4.模板参数的匹配原则

1. 一个非模板函数可以和一个同名的函数模板同时存在,而且该函数模板还可以被实例化为这个非模板函数

// 专门处理int的加法函数
int Add(int left, int right)
{
	return left + right;
}
// 通用加法函数
template<class T>
T Add(T left, T right)
{
	return left + right;
}
int main()
{
	Add(1, 2); // 与非模板函数匹配,编译器不需要特化
	Add<int>(1, 2); // 调用编译器特化的Add版本
	return 0;
}

2. 对于非模板函数和同名函数模板,如果其他条件都相同,在调动时会优先调用非模板函数而不会从该模板产生出一个实例。如果模板可以产生一个具有更好匹配的函数, 那么将选择模板

// 专门处理int的加法函数
int Add(int left, int right)
{
	return left + right;
}
// 通用加法函数
template<class T1, class T2>
T1 Add(T1 left, T2 right)
{
	return left + right;
}
int main()
{
	Add(1, 2); // 与非函数模板类型完全匹配,不需要函数模板实例化
	Add(1, 2.0); // 模板函数可以生成更加匹配的版本,编译器根据实参生成更加匹配的Add函数
	return 0;
}

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



三.   类模板

1.定义格式

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

在看类模板前,我们前看一看我们熟悉的栈

typedef int STDataType;
class Stack
{
	Stack(int capacity = 4)
		:_top(0)
		, _capacity(capacity)
	{
		_a = new STDataType[capacity];
	}

	~Stack()
	{
		delete[] _a;
		_a = nullptr;
		_capacity = _top = 0;
	}
private:
	STDataType* _a;
	int _top;
	int _capacity;
};

这是一个int类型的栈,但若是使用typedef,我们无法实现依照这样一个类来同时实现其他类型(例如double)的栈,我们只能再写一个

typedef double STDataType;
class StackDouble
{
	StackDouble(int capacity = 4)
		:_top(0)
		, _capacity(capacity)
	{
		_a = new STDataType[capacity];
	}

	~StackDouble()
	{
		delete[] _a;
		_a = nullptr;
		_capacity = _top = 0;
	}
private:
	STDataType* _a;
	int _top;
	int _capacity;
};

可以看到,出现的情况与上面函数的使用类似,因此我们也可以使用类模板来解决

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

	~Stack()
	{
		delete[] _a;
		_a = nullptr;
		_capacity = _top = 0;
	}
private:
	T* _a;
	int _top;
	int _capacity;
};

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

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

	~Stack();
private:
	T* _a;
	int _top;
	int _capacity;
};
template <class T>
Stack<T>::~Stack()
{
	delete[] _a;
	_size = _capacity = 0;
}

2.类模板的实例化

类模板实例化与函数模板实例化不同,函数模板中由于可以根据参数的类型来进行隐式实例化,类模板实例化需要在类模板名字后跟<>,然后将实例化的类型放在<>中即可,类模板名字不是真正的类,而实例化的结果才是真正的类。

// Stack类名,Stack<int>才是类型
Stack<int> s1;
Stack<double> s2;


 

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

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

相关文章

2022这特殊的一年,再见!

望着窗外的夕阳以及还未完全融化的积雪&#xff0c;我想是时候给这特殊的一年写篇总结了。于是我翻看了2021年的&#xff0c;发现文末所定的2022年目标。终于明白为什么老人不玩手机可以坐一下午了&#xff0c;因为往事回想起来就和电视连续剧一样。年初参加了开运跑&#xff0…

【UE】pak的mount(带源码解析)

本文使用的引擎版本为UE4.27 为了方便理解&#xff0c;文中选取的代码均为部分截取&#xff0c;只截取与小节相关的部分 文章目录概述几个涉及到的结构Mount时机pak读取优先级目录优先级根据文件名定优先级综上所述概述 正常的散文件加载是使用FFileHelper::LoadFileToArray等…

【阶段四】Python深度学习06篇:深度学习项目实战:卷积神经网络进行狗狗图像分类项目

本篇的思维导图: 项目背景 应用Keras框架构建卷积神经网络进行狗狗图像分类的预测,以及模型的优化。主要用来熟悉Keras卷积层、池化层网络的使用以及模型的优化方法。 数据获取 本次建模数据来源于网络,数据项统计如下: 数据集为狗狗数据集,来自全国各地的狗狗图…

算法进阶指南:第一章练习题

1.The Pilots Brothers refrigerator 牛客竞赛-The Pilots Brothers refrigerator 116. 飞行员兄弟 - AcWing题库 开关问题的特点是每个开关只会作用某个特定范围&#xff0c;所以每个开关最多操作一次&#xff0c;且操作先后次序对最后结果无影响。用16位二进制存储状态&am…

Unity 过场工具(Cutscene)设计(一)

Unity 过场工具(Cutscene)设计&#xff08;一&#xff09; 游戏中通常会涉及到过场内容的制作&#xff0c;从而来进行一些强表现&#xff0c;从而来进行剧情相关的串联&#xff0c;使游戏表现类容更丰富。比较典型的游戏 像原神&#xff0c;天刀等等游戏。 过场工具制作选择 过…

Java程序设计实验2 | Java语言基础(1)

*本文是博主对Java各种实验的再整理与详解&#xff0c;除了代码部分和解析部分&#xff0c;一些题目还增加了拓展部分&#xff08;⭐&#xff09;。拓展部分不是实验报告中原有的内容&#xff0c;而是博主本人自己的补充&#xff0c;以方便大家额外学习、参考。 目录 一、实验…

写在壬寅年末,2023年春节

先回顾过去几年写过的年末总结写在戊戌年末&#xff0c;2019年春节写在己亥年末&#xff0c;2020年春节写在庚子年末&#xff0c;2021年春节写在辛丑年末&#xff0c;2022年春节又一个农历年即将过去&#xff0c;写下这样的年末总结&#xff0c;已经是第5年&#xff0c;于是便有…

Nginx面试题(史上最全 + 持续更新)

尼恩面试宝典专题39&#xff1a;Nginx面试题&#xff08;史上最全、持续更新&#xff09; 本文版本说明&#xff1a;V27 《尼恩面试宝典》升级规划为&#xff1a; 后续基本上&#xff0c;每一个月&#xff0c;都会发布一次&#xff0c;最新版本&#xff0c;可以联系构师尼恩…

本周大新闻|传苹果MR开发样机已送出,比尔盖茨:不太看好Web3和元宇宙

本周大新闻&#xff0c;AR方面&#xff0c;苹果软件曝光“Reality OS”和“xrOS”&#xff1b;IVAS项目仅获批4000万美元&#xff1b;比尔盖茨&#xff1a;不太看好Web3和元宇宙&#xff1b;DigiLens工业AR眼镜ARGO&#xff1b;Kopin拆分部分OLED部门&#xff1b;LetinAR展示塑…

【Linux】gcc/g++的使用

这里写目录标题&#x1f696;gcc如何使用&#x1f696;函数库&#x1f308;动态库和静态库&#x1f696;gcc如何使用 ✒️预处理 预处理功能主要包括宏定义、文件包含、条件编译、去掉注释 实例&#xff1a; gcc -E myfile.c -o myfile.i “-E&#xff08;大写&#xff09;”&…

JVM(二)——参数调优

JVM参数调优 前言 你说你做过JVM调优和参数配置&#xff0c;请问如何盘点查看JVM系统默认值 使用jps和jinfo进行查看 -Xms&#xff1a;初始堆空间 1/64 -Xmx&#xff1a;堆最大值 1/4 -Xss&#xff1a;栈空间-Xms 和 -Xmx最好调整一致&#xff0c;防止JVM频繁进行收集和…

微信小程序项目实例——摇色子

微信小程序项目实例——摇色子 文章目录微信小程序项目实例——摇色子一、项目展示二、核心代码三、效果图文末项目代码见文字底部&#xff0c;点赞关注有惊喜 一、项目展示 摇色子是一款简易的游戏类小程序 用户可以投出1-9个色子 二、核心代码 dice.wxml <!--pages/dic…

编解码标准-H.264

H.264是MPEG-4家族中的一员&#xff0c;即MPEG-4系列文档ISO-14496的第10部分&#xff0c;因此被称作MPEG-4 AVC&#xff0c;MPEG-4重点考虑灵活性和交互性&#xff0c;而H.264着重强调更高的编码压缩率和传输的可靠性。 1、H.264 编码流程 1.1、slice&block 第一步&…

字节面试官: 让你设计一个MQ每秒要抗几十万并发,怎么做?

目录 1、页缓存技术 磁盘顺序写2、零拷贝技术3、最后的总结 这篇文章来聊一下Kafka的一些架构设计原理&#xff0c;这也是互联网公司面试时非常高频的技术考点。 Kafka是高吞吐低延迟的高并发、高性能的消息中间件&#xff0c;在大数据领域有极为广泛的运用。配置良好的Kaf…

电商项目之如何迁移千万级别的数据表

1 背景 电商系统一般都会有一张表记录买家的浏览器信息&#xff0c;包含但不限于浏览器ip、浏览器cookie信息、浏览器user-agent、当前页面的url、当前页面的refer。买家在电商网站上每一次操作&#xff0c;都会记录到该表。该表的数量量至少达到千万级级别。该表有什么用处&a…

07.优雅地断开套接字连接

优雅地断开套接字连接 本章将讨论如何优雅地断开相互连接的套接字。之前用的方法不够优雅是因为&#xff0c;我们是调用close或closesocket函数单方面断开连接的。 基于TCP的半关闭 TCP中的断开连接过程比建立连接过程更重要&#xff0c;因为连接过程中一般不会出现大的变数…

八、MySQL 常用函数汇总(1)

文章目录一、函数1.1 函数简介1.2 不同DBMS函数的差异二、数学函数2.1 绝对值函数ABS(x)和返回圆周率的函数PI()2.2 平方根函数SQRT(x)和求余函数MOD(x,y)2.3 获取整数的函数CEIL(x)、CEILING(x)和FLOOR(x)2.4 获取随机数的函数RAND()和RAND(x)2.5 函数ROUND(x)、ROUND(x,y)和T…

keytool 工具介绍

使用JDK自带的 keytool 工具&#xff1a; 简介 keytool 命令是一个密钥和证书管理的工具。它允许用户使用数字签名管理自己的公钥/私钥对和相关证书&#xff0c;用于自我身份验证(向其他用户和服务验证自己)或数据完整性和身份验证服务。keytool 命令还允许用户缓存通信对等体…

【七牛云 后端】笔试面

一、选择、填空知识点整理 1. fork() 函数 fork() 函数通过系统调用创建一个与原来进程相同的进程&#xff08;如果初始参数或者传入的变量不同&#xff0c;两个进程也可以做不同的事&#xff09; 示例 —— #include <stdio.h> int main() {for(int i0; i<2; i){…

centos7 安装git

一、查看是否安装过git git --version若出现以上版本号&#xff0c;则代表已经安装了git&#xff0c;不需要再次安装了&#xff0c;git安装&#xff0c;分为用yum安装和下载git源码编译安装&#xff0c;以下两种方法&#xff1a; 二、使用yum安装git yum -y install git安装…