【C++】初识模板

news2024/9/24 13:21:01

C++模板入门

    • 一、泛型编程
  • 二、函数模板
    • 1. 函数模板的概念
    • 2. 函数模板格式
    • 3. 函数模板的原理
    • 4. 函数模板的实例化
    • 5. 模板参数的匹配原则
  • 三、类模板

一、泛型编程

假设我们想实现一个交换函数,并且支持不同类型的参数实现,我们可以用 typedef 将类型进行重命名,例如以下代码:

		// 将 int 起别名为 DataType
		typedef int DataType;
		
		void Swap(DataType& x, DataType& y)
		{
			DataType tmp = x;
			x = y;
			y = tmp;
		}
		
		int main()
		{
			DataType x = 0, y = 6;
			Swap(x, y);
		
			return 0;
		}

这样我们每次需要更换类型的时候,只需要更改 int 为其他类型即可;

以上是一种方法,还有一种方法可以使用函数重载实现,例如:

		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. 代码的可维护性比较低,一个出错可能所有的重载均出错。

那能否告诉编译器一个模板,让编译器根据不同的类型利用该模板来生成代码呢?答案是可以的,在这里就需要引入泛型编程,泛型编程: 编写与类型无关的通用代码,是代码复用的一种手段。模板是泛型编程的基础。

模板分为函数模板类模板

二、函数模板

1. 函数模板的概念

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

2. 函数模板格式

在定义函数模板之前,我们需要引入一个关键字:template,它是定义模板的关键字;

使用格式:template<typename T1, typename T2,......,typename Tn>,在 template 关键字后面要用尖括号括住模板参数,模板参数的数量可以是任意的,但是需要使用 typename 关键字来定义模板参数,也可以使用 class(切记:不能使用struct代替class)。

例如交换函数的函数模板:

		template<typename T>
		void Swap(T& t1, T& t2)
		{
			T tmp = t1;
			t1 = t2;
			t2 = tmp;
		}

3. 函数模板的原理

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

例如下图就很好地体现了这一个过程:

在这里插入图片描述

在编译器编译阶段,对于模板函数的使用,编译器需要根据传入的实参类型来推演生成对应类型的函数以供调用。

比如:当用 double 类型使用函数模板时,编译器通过对实参类型的推演,将 T 确定为 double 类型,然后产生一份专门处理 double 类型的代码,对于字符类型也是如此,即编译器用模板实例化生成对应的Swap 函数。

4. 函数模板的实例化

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

隐式实例化: 让编译器根据实参推演模板参数的实际类型,例如以下这个 Add 的函数模板,实现两个数的相加:

		template<class T>
		T Add(T a, T b)
		{
			return a + b;
		}
		
		int main()
		{
			int a = 10, b = 20;
			double c = 1.11, d = 2.22;
		
			cout << "sum = " << Add(a, b) << endl;
			cout << "sum = " << Add(c, d) << endl;
		
			return 0;
		}

上面的两个调用实例化都没有问题,编译器进行了隐式实例化,运行的结果如下:

在这里插入图片描述

但是如果这样调用会编译通过吗:Add(a, d),答案是不行的,通过实参 a 将 T 推演为 int,通过实参 dT 推演为 double 类型,但模板参数列表中只有一个 T, 编译器无法确定此处到底该将 T 确定为 int 或者 double 类型而报错。

所以此时有两种解决方法:

  1. 用户自己来强制转化
  2. 使用显式实例化

如果自己来强制转化,就可以使用以下方法:

		int main()
		{
			int a = 10, b = 20;
			double c = 1.11, d = 2.22;
			
			cout << "sum = " << Add((double)a, d) << endl;
			cout << "sum = " << Add(a, (int)d) << endl;
		
			return 0;
		}

我们可以在调用 Add 函数时,将 a 强转为 double,或者将 d 强转为 int

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

例如上面的问题中,我们使用显式实例化解决,代码如下:

		int main()
		{
			int a = 10, b = 20;
			double c = 1.11, d = 2.22;
		
			cout << "sum = " << Add(a, b) << endl;
			cout << "sum = " << Add(c, d) << endl;
		
			cout << "sum = " << Add<double>(a, d) << endl;
			cout << "sum = " << Add<int>(a, d) << endl;
		
			return 0;
		}

我们在函数名的后面用尖括号指定了模板参数的类型,这就是显式实例化。

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

5. 模板参数的匹配原则

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

例如以下两段代码:

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

三、类模板

假设我们我们需要实现一个通用的栈,我们可以使用 typedef 关键字对类型起别名,每次需要改变类型的时候,只需要在 typedef 更改即可,例如以下的 Stack 类:

		typedef int DataType;
		class Stack
		{
		public:
			Stack(size_t capacity = 4)
			{
				_array = new DataType[capacity];
		
				_capacity = capacity;
				_size = 0;
			}
		
			~Stack()
			{
				cout << "~Stack()" << endl;
		
				delete[] _array;
				_array = nullptr;
				_size = _capacity = 0;
			}
		
		private:
			// 内置类型
			DataType* _array;
			int _capacity;
			int _size;
		};

虽然以上的 Stack 类不同的类型只需要改变 typedef 的类型即可,但是如果我同时需要两个栈,一个栈的参数是 int ,另一个栈的参数是 double 呢,上面的方法就不能很好地满足了,所以我们引入类模板。

类模板的使用如下,以 Stack 类为例:

		template<class T>
		class Stack
		{
		public:
			Stack(size_t capacity = 4)
			{
				_array = new T[capacity];
		
				_capacity = capacity;
				_size = 0;
			}
		
			~Stack()
			{
				delete[] _array;
				_array = nullptr;
				_size = _capacity = 0;
			}
		private:
			T* _array;
			int _capacity;
			int _size;
		};

实例化对象如下:

		int main()
		{
			Stack<int> st1;
			Stack<double> st2;
		
			return 0;
		}

注意,Stack 是类名,Stack<int>Stack<double> 才是类型;template 的作用范围是 Stack 这个类。

这样我们就同时实现了两个栈,一个栈存放的参数是 int,另外一个存放的是 double

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

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

相关文章

V2MOM工作法

V2MOM分别代表愿景&#xff08;vision&#xff09;、价值&#xff08;values&#xff09;、方法&#xff08;methods&#xff09;、障碍&#xff08;obstacles&#xff09;、衡量指标&#xff08;measurement&#xff09;。 第一&#xff0c;我真正想要的是什么呢&#xff1f;这…

ubuntu磁盘管理

show partition information 挂载设备在这 显示文件系统信息 build file system mkfs -t ext4 /dev/nvme0n1p4命令作用&#xff1a;将/dev/nvme0n1p4 格式化为 ext4 建立交换分区 mkswap -c -v1 /dev/nvme0n1p4 102400-c&#xff1a;check -v1&#xff1a;新版交换分区 -v0&…

四、web应用程序技术——HTTP

文章目录 1 HTTP请求2 HTTP响应3 HTTP方法4 URL5 HTTP消息头5.1 常用消息头5.2 请求消息头5.3 响应消息头 6 cookie7 状态码8 HTTP代理9 HTTP身份验证 HTTP&#xff08;HyperText Transfer Protocol&#xff0c;超文本传输协议&#xff09;是访问万维网使用的核心通信协议&…

Linux 1.2.13 -- IP分片重组源码分析

Linux 1.2.13 -- IP分片重组源码分析 引言为什么需要分片传输层是否存在分段操作IP分片重组源码分析ip_createip_findip_frag_createip_doneip_glueip_freeip_expireip_defragip_rcv 总结 本文源码解析参考: 深入理解TCP/IP协议的实现之ip分片重组 – 基于linux1.2.13 计网理论…

2023Android面试,如果想卷请继续。备战金九银十

随着移动互联网的快速发展&#xff0c;Android开发岗位竞争也越来越激烈。作为一名Android程序员&#xff0c;面试是进入理想公司的重要一步。本文将分析市场对Android开发岗位的需求&#xff0c;分析2022年的Android开发岗位面试情况&#xff0c;并总结出历年来常见的面试题目…

MySQL中同比和环比语句如何写?

营收表如下&#xff08;表名&#xff1a;a&#xff09;如下图&#xff1a; 营收表 year month money 2021 1 1000 2021 2 1200 2022 1 1300 2022 2 1500 需要算出2022年营收同比与环比&#xff1a; 同比&#xff1a;和去年同月相比&#xff08;1300-1000/1000*100%&#xff0…

Python类的设计

Python类的设计 # 定义一个闹钟类 class Clock:__cureen_keyNone # 私有成员不能改变和使用def __init__(self, id, price): # 类对象是立即自动执行self.id idself.price pricedef ring(self):import winsound # 内置声音方法winsound.Beep(2000,3000)clock1 Clock(…

自然语言处理学习笔记(六)————字典树

目录 1.字典树 &#xff08;1&#xff09;为什么引入字典树 &#xff08;2&#xff09;字典树定义 &#xff08;3&#xff09;字典树的节点实现 &#xff08;4&#xff09;字典树的增删改查 DFA&#xff08;确定有穷自动机&#xff09; &#xff08;5&#xff09;优化 1.…

Python基础--序列操作/函数

Python基础 1.序列的操作 2.函数 1. 数据类型的具体操作 1.1 序列操作--列表具体操作&#xff1a; #定义列表 listA [] #定义一个空列表 listB [1,2.8,"你好",listA,[1,2,3]] # 访问列表 print(listB)#查看整个列表 print(listB[2])#查看单个…

docker 安装mongodb 虚拟机安装mongodb

生产环境直接安装比较好&#xff0c;以及使用集群环境&#xff0c;本文仅测试交流使用&#xff0c;我用来写分布式im测试使用&#xff1a; nami-im: 分布式im, 集群 zookeeper netty kafka nacos rpc主要为gate&#xff08;长连接服务&#xff09; logic &#xff08;业务&…

MySQL:内置函数、复合查询和内外连接

内置函数 select 函数; 日期函数 字符串函数 数学函数 其它函数 复合查询&#xff08;多表查询&#xff09; 实际开发中往往数据来自不同的表&#xff0c;所以需要多表查询。本节我们用一个简单的公司管理系统&#xff0c;有三张 表EMP,DEPT,SALGRADE来演示如何进行多表查询…

设计模式行为型——访问者模式

目录 访问者模式的定义 访问者模式的实现 访问者模式角色 访问者模式类图 访问者模式举例 访问者模式代码实现 访问者模式的特点 优点 缺点 使用场景 注意事项 实际应用 访问者模式的定义 访问者模式&#xff08;Visitor Pattern&#xff09;属于行为型设计模式&am…

总结950

7:00起床 7:30~8:00复习单词300个&#xff0c;记忆100个 8:10~9:30数学660&#xff0c;只做了10道题&#xff0c;发现对各知识点的掌握程度不一。有些熟练&#xff0c;有些生疏 9:33~10:25计算机网络课程1h 10:32~12:02继续660&#xff0c;也不知道做了几道 2:32~4:00数据…

Node.js |(四)HTTP协议 | 尚硅谷2023版Node.js零基础视频教程

学习视频&#xff1a;尚硅谷2023版Node.js零基础视频教程&#xff0c;nodejs新手到高手 文章目录 &#x1f4da;HTTP概念&#x1f4da;窥探HTTP报文&#x1f4da;请求报文的组成&#x1f407;HTTP请求行&#x1f407;HTTP请求头&#x1f407;HTTP的请求体 &#x1f4da;响应报文…

阔别三年,领先回归!别克LPGA锦标赛申城十月再启高球盛会

2023年8月4日——2023年金秋十月&#xff0c;阔别中国赛场已久的别克LPGA锦标赛将强势归来&#xff0c;于10月12日至15日在上海旗忠花园高尔夫俱乐部再次拉开帷幕。作为三年来首个回归、同时也是今年国内唯一开赛的国际顶级高尔夫职业赛事&#xff0c;别克LPGA锦标赛将吸引全世…

零代码集成融云连接更多应用

融云是安全、可靠的全球互联网通信云服务商&#xff0c;向开发者和企业提供即时通讯和实时音视频通信云服务。 场景描述&#xff1a; 基于融云的开放api能力&#xff0c;无代码集成融云平台的音视频通话、即时通信、聊天室、短信等业务&#xff0c;使融云连通其它应用。通过A…

Mirror网络库 | 说明

此篇为上文&#xff0c;下篇&#xff1a;Mirror网络库 | 实战 一、介绍 基于UNET&#xff0c;从2014年经过9年实战测试&#xff1b;服务器和客户端是一个项目&#xff1b;使用NetworkBehaviour而不是MonoBehaviour&#xff0c;还有NetworkServer和NetworkClient&#xff1b;Mi…

nodejs安装ffi报错,windows-build-tools安装不成功

首先要确定nodejs的版本 要使用v17.x.x版本的nodejs Index of /dist/latest-v17.x/&#xff0c;才能安装windows-build-tools npm install --global --production windows-build-tools 执行命令 他会去下载很多编译需要用的文件。一方面是python27&#xff0c;另一方面是B…

双周赛110(模拟、枚举+哈希表)

文章目录 双周赛110[2806. 取整购买后的账户余额](https://leetcode.cn/problems/account-balance-after-rounded-purchase/)模拟 [2807. 在链表中插入最大公约数](https://leetcode.cn/problems/insert-greatest-common-divisors-in-linked-list/)模拟 [2808. 使循环数组所有元…

Linux 终端操作命令(2)内部命令分类

Linux 终端操作命令 也称Shell命令&#xff0c;是用户与操作系统内核进行交互的命令解释器&#xff0c;它接收用户输入的命令并将其传递给操作系统进行执行&#xff0c;可分为内部命令和外部命令。内部命令是Shell程序的一部分&#xff0c;而外部命令是独立于Shell的可执行程序…