【C++】模板的一点简单介绍

news2024/9/20 21:46:36

模板

    • 前言
    • 泛型编程
    • 函数模板
      • 概念
      • 格式
      • 函数模板的原理
      • 函数模板的实例化
    • 类模板
      • 类模板的定义格式
      • 类模板的实例化

在这里插入图片描述

前言

这篇博客讲的是模板的一些基本知识,并没有那么深入,但是如果你是为了过期末考试而搜的这篇博客,我觉得下面讲的是够了的。

之后会再出一篇深入讲解模板的博客。

泛型编程

先说一个例子:

如何实现一个通用的交换函数呢?

学了C++之后,我们有了函数重载这个东西。这是C不具有的语法。
我们可以通过函数重载来实现相同函数名交换不同类型的变量。像下面这样:

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

当然用C也可以,但是C实现的话,只能搞成不同函数名来实现不同变量的交换,而且还没有引用的语法。

但是不论是上面的函数重载还是用C来实现,都好麻烦,当我们每次想要交换新类型的交换的时候都要再重写一个函数。

也就是下面的缺点:

  1. 重载的函数仅仅是类型不同,代码复用率比较低,只要有新类型出现时,就需要用户自己增加对应的函数
  2. 代码的可维护性比较低,一个出错可能所有的重载均出错

活字印刷,相信大家都听过。

在这里插入图片描述

通过改变模具中的字来印刷出不同的文章。

那能否告诉编译器一个模具,让编译器根据不同的类型利用该模具来生成代码呢?

如果在C++中,也能够存在这样一个模具,通过给这个模具中填充不同文字块(类型),来获得不同文字的文章(即生成具体类型的代码)。

这就是本篇博客要讲的模板。

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

模板分为两类,函数模板和类模板。
在这里插入图片描述

函数模板

概念

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

格式

//写函数前要加这个
template<typename T1, typename T2,......,typename Tn>
//typename后面的T1,这种就相当于我们定义变量时候的变量名,是
//随便取的,只不过这里是类型名。常用的还有K,V,跟T合起来就是KTV。

//然后再写函数
返回值类型 函数名(参数列表){}

来个交换的例子:
在这里插入图片描述
下面都是OK的。
在这里插入图片描述

typename是用来定义模板参数关键字,也可以使用class(切记:不能使用struct代替class)。

在这里插入图片描述
目前阶段,记住写成typename和class都是可以的。

再来个交换int和交换double的例子:
在这里插入图片描述
但是记住,上面的代码中,交换int和交换double走的不是一个函数。

函数模板的原理

大家都知道,瓦特改良蒸汽机,人类开始了工业革命,解放了生产力。机器生
产淘汰掉了很多手工产品。本质是什么,重复的工作交给了机器去完成。

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

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

如何证明呢?
看图(稍微懂一点反汇编就行):
在这里插入图片描述
右侧的反汇编中,交换int和交换double的函数地址是不一样的。

其实也可以对应到活字印刷术当中,我们想要有一份文章的拷贝,不是用活字印刷里面的木头块来读的,而是通过那些木头块来把文章印到纸上来读的。

所以上面的就很好理解了,不是用模板,而是用模板来实例化出对应的函数来使用。

其实,C++标准库中也是有交换的函数的,只不过用起来是小写swap。
在这里插入图片描述
可以看到,标准库中实现的就是用的模板。
我们可以直接使用:
在这里插入图片描述

函数模板的实例化

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

  1. 隐式实例化:让编译器根据实参推演模板参数的实际类型
    先给个能跑的例子:编译器自动推演在这里插入图片描述
    再给个跑不了的例子:
    在这里插入图片描述
    出现上面问题的原因是a和d1的类型是不相同的,而模板函数中两个参数的类型是相同的,但是我们偏要生成一个int和double的函数,这样是生成不了的。因为编译器不能决定到底是用int还是用double来实现。编译器就犯难了,只能报错,不然就得背黑锅。

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

但是我们之前写的函数是可以进行隐式类型转换的。比如说下面这样:
在这里插入图片描述
如果想用模板来实现两个不同类型相加的时候,可用以下方法:
在这里插入图片描述
但是这样非常挫。

可以用下面的显示实例化来实现。

  1. 显示实例化:在函数名后的<>中指定模板参数的实际类型
    在这里插入图片描述
    这个方法在函数模板中不是那么常用,但是在类模板中就非常常用了。
    再给个在函数模板中适用的场景。
    在这里插入图片描述

上面不能直接使用func,因为模板中的T是不确定的。
这时候就要用到<>了。
在这里插入图片描述

当我们同时拥有模板和一个功能相同的加法函数的时候会发生什么?
在这里插入图片描述
会像上面这样,但是上面的写法是很挫的,不建议同时出现功能相同的函数模板。

类模板

类模板的定义格式

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

和函数模板很像。

类模板的是使用场景是什么呢?
我们来写一个简单的类模板。

在这里插入图片描述

如果想将类模板中的的成员函数在类外定义的话,得像这样:
在这里插入图片描述

还要说一点就是模板不支持分离编译。 声明放在.h 定义放在.cpp。
至于为什么的话放到后面深入讲模板的博客中说。

类模板的实例化

在这里插入图片描述
如果不指明T的类型,是会报错的。
那么用类模板的时候就要显示指出T的类型。
在这里插入图片描述

最后再给一个顺序表的例子:

直接把代码给出来:

template<typename T>
class Stack
{
public:
	Stack(size_t capacity = 4)
		:_data(nullptr)
		,_top(0)
		,_capacity(0)
	{
		if (capacity > 0)
		{
			_data = new T[capacity];
			_top = 0;
			_capacity = 4;
		}
	}

	~Stack()
	{
		delete[] _data;
		_data = nullptr;
		_top = _capacity = 0;
	}

	void Push(const T& a)
	{
		if (_capacity == _top)
		{
			int newcapacity = _capacity == 0 ? 4 : 2 * _capacity;
			
			T* newdata = new T[newcapacity];
			if (_data)
			{
				memcpy(newdata, _data, sizeof(T) * _capacity);
				delete[] _data;
			}
			_data = newdata;
			_capacity = newcapacity;
		}
		_data[_top] = a;
		++_top;
	}

	void Pop()
	{
		assert(_top);
		--_top;
	}

	bool Empty()
	{
		return _top == 0;
	}

	const T& Top()
	{
		return _data[_top - 1];
	}

private:
	T* _data;
	size_t _top;
	size_t _capacity;
};

int main()
{
	Stack<int> s;
	s.Push(1);
	s.Push(2);
	s.Push(3);
	s.Push(4);
	s.Push(5);

	while (!s.Empty())
	{
		cout << s.Top() << " ";
		s.Pop();
	}
	cout << endl;

	return 0;
}

其中最重要的是下面main函数中的Stack<int> s; 这条语句。

到此结束。。。

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

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

相关文章

简单分享线程池的设计

温故而知新&#xff0c;可以为师矣。 线程池是什么 线程池&#xff08;Thread Pool&#xff09;是一种基于池化思想管理线程的工具&#xff0c;经常出现在多线程服务器中&#xff0c;如MySQL。 池化思想&#xff0c;就是为了提高对资源的利用率&#xff0c;减少对资源的管理&a…

MySQL---空间索引、验证索引、索引特点、索引原理

1. 空间索引 MySQL在5.7之后的版本支持了空间索引&#xff0c;而且支持OpenGIS几何数据模型 空间索引是对空间数据类型的字段建立的索引&#xff0c;MYSQL中的空间数据类型有4种&#xff0c;分别是&#xff1a; 类型 含义 说明 Geometry 空间数据 任何一种空间类型 Poi…

HCIA-VRP系统

目录 一&#xff0c;什么是VRP VRP提供的功能&#xff1a; VRP文件系统&#xff1a; VRP存储设备&#xff1a; 设备初始化过程&#xff1a; 设备管理方式&#xff1a; 1&#xff0c;Web界面&#xff1a;可视化操作&#xff0c;通过http和https登录&#xff08;192.168.1.…

信息安全工程复习

目录 第二章 从口令系统说起 2.1 身份鉴别常见手段及例子 2.2 多因子认证 2.3 计时攻击 2.4 口令机制 2.5 假托和钓鱼 第三章 安全协议 3.1 认证协议 3.2 安全协议攻击 3.3 密钥分配协议 3.4 课后作业 第四章 访问控制 4.1 概念 4.2 控制访问三要素 4.3 控制访问…

Windows服务

参考地址&#xff1a;https://www.cnblogs.com/2828sea/p/13445738.html 1. 新建服务 2. 在 service 下 添加安装程序 会自动添加 修改这两个文件属性&#xff1a; serviceInstaller1&#xff1a; DelayedAutoStart:是否自动启动Descrition:介绍服务&#xff08;自定义&…

chatgpt赋能Python-python3_图片处理

Python3图片处理&#xff1a;简单高效的图像处理工具 Python3作为一种高级编程语言&#xff0c;在科学、金融、工程等领域中广受欢迎。它具有简洁的语法、快速的开发速度、多样化的应用场景等特点。其中&#xff0c;Python3在图像处理方面也非常出色&#xff0c;本文将介绍Pyt…

pg事务:事务的处理

事务的处理 事务块 从事务形态划分可分为隐式事务和显示事务。隐式事务是一个独立的SQL语句&#xff0c;执行完成后默认提交。显示事务需要显示声明一个事务&#xff0c;多个sql语句组合到一起称为一个事务块。 事务块通过begin&#xff0c;begin transaction&#xff0c;st…

【学习日记2023.5.20】 之 菜品模块完善

文章目录 3. 功能模块完善之菜品模块3.1 公共字段自动填充3.1.1 问题分析3.1.2 实现思路3.1.3 代码开发1.3.1 步骤一1.3.2 步骤二1.3.3 步骤三 3.1.4 功能测试3.1.5 提交代码 3.2 新增菜品3.2.1 需求分析与设计3.2.2 代码开发3.2.2.1 文件上传实现3.2.2.2 新增菜品实现 3.2.3 功…

pg事务:快照

pg中的快照 快照&#xff08;snapshot&#xff09;是记录数据库当前瞬时状态的一个数据结构。pg数据库的快照保存当前所有活动事务的最小事务ID、最大事务ID、当前活跃事务列表、当前事务的command id等 快照数据保存在SnapshotData结构体类型中&#xff0c;源码src/include/u…

PyQt5桌面应用开发(16):定制化控件-QPainter绘图

本文目录 PyQt5桌面应用系列画画图&#xff0c;喝喝茶QPainter和QPixmapQPixmapQPainter绘制事件 一个魔改的QLabelCanvas类主窗口主程序&#xff1a; 总结 PyQt5桌面应用系列 PyQt5桌面应用开发&#xff08;1&#xff09;&#xff1a;需求分析 PyQt5桌面应用开发&#xff08;2…

深入了解vector

vector 1. vector的介绍及使用1.1 vector的介绍1.2 vector的使用1.2.1 vector的定义&#xff08;(constructor)构造函数声明&#xff09;1.2.2 vector iterator 的使用1.2.3 vector Capacity1.2.4 vector Modifiers1.2.4 vector 迭代器失效问题 2. vector模拟实现 1. vector的介…

快速排序的三种方法

今日复习了一下快速排序的算法。 hoare法 快速排序由Hoare在1960年提出。它的基本思想是&#xff1a;通过排序将需要排序的数据分割成独立的两部分&#xff0c;左边的所有数据都比右边的小&#xff0c;然后再按此方法对这两部分数据分别进行快速排序递归&#xff0c;使其变成有…

时间序列预测 | 基于秃鹰算法优化BP神经网络(BES-BP)的时间序列预测,matlab代码

文章目录 效果一览文章概述部分源码参考资料效果一览 文章概述 基于秃鹰算法优化BP神经网络(BES-BP)的时间序列预测,matlab代码 评价指标包括:R2、MAE、MSE、RMSE等,代码质量极高,方便学习和替换数据。 部分源码 %% 清空环境变量 warning off % 关闭报警信息…

BurpSuite—-Spider模块(蜘蛛爬行)

本文主要介绍BurpSuite—-Spider模块(蜘蛛爬行)的相关内容 关于BurpSuite的安装可以看一下之前这篇文章&#xff1a; http://t.csdn.cn/0Qw2n 一、简介 Burp Spider 是一个映射 web 应用程序的工具。它使用多种智能技术对一个应用程序的内容和功能进行全面的清查。 Burp Spi…

基于Qt+FFmpeg的视频监控系统

github源码 需求分析 假设一个业务场景&#xff1a;每个员工工位旁有两个网络摄像头。老板需要一个员工监控软件&#xff0c;在上班时软件可以拉取RTSP视频流&#xff0c;也可以随时录制视频。这样老板就可以知道谁在摸鱼了 ◕‿◕ 为防有人上纲上线&#xff0c;在此特别声明…

【Redis】聊一下缓存双写一致性

缓存虽然可以提高查询数据的的性能&#xff0c;但是在缓存和数据 进行更新的时候 其实会出现数据不一致现象&#xff0c;而这个不一致其实可能会给业务来带一定影响。无论是Redis 分布式缓存还是其他的缓存机制都面临这样的问题。 数据不一致是如何发生&#xff1f; 数据一致…

【c语言】文件复制原理

创作不易&#xff0c;本篇文章如果帮助到了你&#xff0c;还请点赞 关注支持一下♡>&#x16966;<)!! 主页专栏有更多知识&#xff0c;如有疑问欢迎大家指正讨论&#xff0c;共同进步&#xff01; &#x1f525;c语言系列专栏&#xff1a;c语言之路重点知识整合 &#x…

pg事务:事务ID

事务ID pg中每个事务都会分配事务ID&#xff0c;事务ID分为虚拟事务ID和持久化事务ID&#xff08;transactionID&#xff09;。pg的事务ID非常重要&#xff0c;是理解事务、数据可见性、事务ID回卷等等的重要知识点。 虚拟事务ID 只读事务不会分配事务ID&#xff0c;事务ID是…

【我的C++入门之旅】(下)

前言 参考前章内容【我的C入门之旅】(上) 目录 前言1.引用常引用传值、传引用效率比较引用和指针的区别 2.auto关键字使用场景 3.范围for 语法糖4.inline函数5.指针空值nullptr 1.引用 取别名&#xff0c;一块空间有多个名字或者说是一个变量有多个名字 比如&#xff1a;李逵&…

【LeetCode: 44. 通配符匹配 | 暴力递归=>记忆化搜索=>动态规划 】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…