【C++】初识模板

这篇具有很好参考价值的文章主要介绍了【C++】初识模板。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、泛型编程

假设我们想实现一个交换函数,并且支持不同类型的参数实现,我们可以用 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. 函数模板的原理

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

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

【C++】初识模板,c++,算法,开发语言

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

比如:当用 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;
		}

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

【C++】初识模板,c++,算法,开发语言

但是如果这样调用会编译通过吗: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文章来源地址https://www.toymoban.com/news/detail-635772.html

到了这里,关于【C++】初识模板的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【C++初阶】八、初识模板(泛型编程、函数模板、类模板)

    ========================================================================= 相关代码gitee自取 : C语言学习日记: 加油努力 (gitee.com)  ========================================================================= 接上期 : 【C++初阶】七、内存管理 (C/C++内存分布、C++内存管理方式、operator new / delete 函数、定位new表

    2024年02月04日
    浏览(46)
  • learn C++ NO.8——初识模板(函数模板、类模板)

    现在是北京时间2023年6月5号13.31分,距离上一篇博客发布已过一周。期间还零零散散进行了一些期末考试,这也说明了我的大一时光快要结束了。我也想抓着期末的尾巴,好好的复习一下前面的所学内容,争取这周能够更一下简单数据结构的博客。 泛型编程是一种编程范式,

    2024年02月07日
    浏览(50)
  • 【C++初阶】内存管理 && 初识模板

    C/C++的内存分布主要分为 栈区、堆区、数据段和代码段,还有内存映射段。 栈又叫堆栈–非静态局部变量/函数参数/返回值等等,栈是向下增长的。 内存映射段是高效的I/O映射方式,用于装载一个共享的动态内存库。用户可使用系统接口创建共享共享内存,做进程间通信。

    2024年02月04日
    浏览(46)
  • HarmonyOS(二)—— 初识ArkTS开发语言(上)之TypeScript入门

    Mozilla创造了JS,Microsoft创建了TS,而Huawei进一步推出了ArkTS。因此在学习使用ArkTS前,需要掌握基本的TS开发技能。 ArkTS是HarmonyOS优选的主力应用开发语言。它在TypeScript(简称TS)的基础上,匹配ArkUI框架,扩展了声明式UI、状态管理等相应的能力,让开发者以更简洁、更自然的

    2024年02月06日
    浏览(54)
  • [C++开发 03 _ 模板(167)]

    1.1模板的概念  模板的特点: 模板不可以直接使用,它只是一个框架 模板的通用并不是万能的 1.2函数模板 1.2.1函数模板语法 函数模板的引入: 函数模板的举例:  总结: 函数模板利用template 使用函数模板有两种方式:1、自动类型推导     2、显示指定类型 模板的

    2024年01月20日
    浏览(39)
  • HarmonyOS(二)—— 初识ArkTS开发语言(中)之ArkTS的由来和演进

    在上一篇文章HarmonyOS(二)—— 初识ArkTS开发语言(上)之TypeScript入门,我初识了TypeScript相关知识点,也知道ArkTS是华为基于TypeScript发展演化而来。 从最初的基础的逻辑交互能力,到具备类型系统的高效工程开发能力,再到融合声明式UI、多维状态管理等丰富的应用开发能

    2024年02月06日
    浏览(59)
  • 【C++算法模板】图论-拓扑排序,超详细注释带例题

    推荐视频链接:D01 拓扑排序 给定一张 有向无环图 ,排出所有顶点的一个序列 A A A 满足:对于图中的每条有向边 ( x , y ) (x,y) ( x , y ) , x x x 在 A A A 中都出现在 y y y 之前,则称 A A A 是该图的顶点的一个拓扑序 拓扑排序 可以判断有向图中是否有环,可以生成拓扑序列 对于下

    2024年04月15日
    浏览(41)
  • HarmonyOS(二)—— 初识ArkTS开发语言(下)之ArkTS声明式语法和组件化基础

    通过前面ArkTS开发语言(上)之TypeScript入门以及ArkTS开发语言(中)之ArkTS的由来和演进俩文我们知道了ArkTS的由来以及演进,知道了ArkTS具备了声明式语法和组件化特性,今天,搭建一个可刷新的排行榜页面。在排行榜页面中,使用循环渲染控制语法来实现列表数据渲染,使

    2024年02月04日
    浏览(58)
  • 初识Go语言25-数据结构与算法【堆、Trie树、用go中的list与map实现LRU算法、用go语言中的map和堆实现超时缓存】

      堆是一棵二叉树。大根堆即任意节点的值都大于等于其子节点。反之为小根堆。   用数组来表示堆,下标为 i 的结点的父结点下标为(i-1)/2,其左右子结点分别为 (2i + 1)、(2i + 2)。 构建堆   每当有元素调整下来时,要对以它为父节点的三角形区域进行调整。 插入元素

    2024年02月12日
    浏览(59)
  • C++、STL标准模板库和泛型编程 ——迭代器、 算法、仿函数(侯捷)

    侯捷 C++八部曲笔记汇总 - - - 持续更新 ! ! ! 一、C++ 面向对象高级开发 1、C++面向对象高级编程(上) 2、C++面向对象高级编程(下) 二、STL 标准库和泛型编程 1、分配器、序列式容器 2、关联式容器 3、迭代器、 算法、仿函数 4、适配器、补充 三、C++ 设计模式 四、C++ 新标准 五、

    2023年04月25日
    浏览(52)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包