【C++模版初阶】——我与C++的不解之缘(七)

news2024/9/20 6:25:45

一、泛型编程

        在之前,我们想要实现一个交换两个值的函数,我们要实现好多个(int类型、double类型、char类型以及自定义类型)对于每一个类型,我们都需要实现一个交换函数:

int Swap(int& x, int& y)
{
	int tmp = x;
	x = y;
	y = tmp;
}
double Swap(double& x, double& y)
{
	double tmp = x;
	x = y;
	y = tmp;
}
char Swap(char& x, char& y)
{
	char tmp = x;
	x = y;
	y = tmp;
}

       实现不同类型的交换,我们可以使用函数重载,写出来好多个;

但是,这也有不足之处:

1、函数重载只是参数类型不同,代码复用率比较低,只要有新的类型,就需要用户自己添加对应的函数。

2、代码可维护性较低,一个出错可能所以的重载都出错。

        那我们可不可以只实现一个模版,让编译器根据不同的类型利用我们写的模版来自动生成相对应 的函数呢?

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

二、函数模版

        2.1、函数模版概念

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

        2.2、函数模版格式

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

        返回值类型 函数名(函数参数){}

有了模版,对于上面的交换函数我们就可以这样写:

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

注意:这里typename 是定义模版参数的关键字,也可以使用class (不能用struct 代替class)

        2.3、函数模版原理

        对于函数模版,它只是一个蓝图,不是一个函数,是编译器使用方式特定具体类型函数的一个模具;

        函数模版其实就是把我们之前重复做的事情交给了编译器。

        编译器会根据参数类型生成不同的函数,这里可以通过汇编代码看一下:

#include<iostream>

template<typename T>
void Swap(T& x, T& y)
{
	T tmp = x;
	x = y;
	y = tmp;
}
int main()
{
	int a = 1, b = 2;
	Swap(a, b);

	double c = 1.1, d = 2.2;
	Swap(c, d);
	return 0;
}

        这里可以看到,编译器根据不同的参数,生成了不同的函数,Swap<int>、Swap<double>

        2.4、函数模版实例化

        在使用不同类型的参数使用函数模版时,称为函数模版的实例化。

模版的实例化又分为:隐式实例化和显式实例化。

2.4.1、隐式实例化

        所谓隐式实例化,就是让编译器根据实参推演模版参数的实际类型

但是这样也会遇到一些问题,就比如如下函数:

template<typename T>
T Add(const T& x, const T& y)
{
	return x + y;
}

int main()
{
	int a = Add(1, 2); //两个int类型
	int b = Add(1.1, 2.2); //两个double类型
	int c = Add(1, 2.2);  // int double 类型
	return 0;
}

    

如上述情况,编译器会报错:没有与参数列表匹配的函数模版。

        在编译期间,当编译器看到该实例化时,需要推演其实参类型通过实参1 将T推演为int,通过实参 2.2  将T推演为double类型,但模板参数列表中只有一个T编译器无法确定此处到底该将T确定为int 或者 double类型而报错。

解决这个问题有两种方法,        

        1、强制类型转换。

        2、模版函数显示实例化

2.4.2、显示实例化

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

int main(void)
{
	int a = 10;
	double b = 20.0;

	// 显式实例化
	Add<int>(a, b);
	return 0;
}

        2.5、模版参数的匹配原则

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

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

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

三、类模版

        3.1、类模版的定义格式

        

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

        在之前,我们实现的数据结构,线性表、栈和队列等中,我们只能存储一种类型的数据。如果我们想要存储其他类型,就要重新实现一个数据结构;有了模版,我们只需要写出来一个模具,让编译器自动生成存储不同类型的类。

这里简单实现一下。

//类模版
template<typename T>
class Stack
{
public:
	Stack(int capacity = 4)
	{
		_arr = new T[capacity];
		_top = 0;
		_capacity = capacity;
	}
	~Stack()
	{
		delete[] _arr;
		_arr = nullptr;
		_top = _capacity = 0;
	}
	void Push(T& x);

	void Pop();
	bool Empty()
	{
		return _top == 0;
	}
private:
	T* _arr;
	size_t _top;
	size_t _capacity;
};
template<typename T>
void Stack<T>::Push(T& x)
{
	if (_top >= _capacity)
	{
		T* tmp = (T*)realloc(_arr, sizeof(T) * 2 * _capacity);
		if (tmp == nullptr)
		{
			exit(1);
		}
		_arr = tmp;
		_capacity *= 2;
	}
	_arr[_top++] = x;
}
template<typename T>
void Stack<T>::Pop()
{
	_top--;
}

        3.2、类模版的实例化

        类模版实例化和函数模版的实例化不同,类模版实例化需要在类模版名字后面跟<>,然后将实例化的类型放在<>中即可。

注意:类模版名字不是真正的类,实例化的结果才是真正的类。

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

扩展:

        类模版在声明和定义分离时(类里面声明,类外面定义)与普通的类不同:

类里面声明:

类外面定义:

模版初阶大致内容就这些,后面还会深入学习模版的内容。

感谢各位大佬支持并指出问题,

                        如果本篇内容对你有帮助,可以一键三连支持以下,感谢支持!!!

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

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

相关文章

【Qt】仿照qq界面的设计

widget.h #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QDebug>//QT中信息调试类&#xff0c;用于输出数据&#xff0c;无需使用该类的实例化对象&#xff0c;直接使用成员函数即可 #include <QIcon>//图标类 #include <QPushButton&…

【笔记】数据结构刷题09

快速排序 215. 数组中的第K个最大元素 class Solution { public:int findKthLargest(vector<int>& nums, int k) {return divide(nums,0,nums.size()-1,nums.size()-k);}int divide(vector<int>& nums,int left,int right,int k){if(leftright)return nums…

Linux 如何将pwd的结果同步复制到剪贴板

pwd是我们经常使用的一个命令&#xff0c;它的作用是显示当前所在路径。 不过&#xff0c;很多时候我们使用pwd命令不仅仅是查看当前路径&#xff0c;而是复制路径。 通常情况下&#xff0c;使用pwd命令进行复制路径的操作如下&#xff1a; 使用pwd显示路径&#xff08;键盘…

计算机毕业设计选题推荐-健康健身追踪系统-运动健身系统-Java/Python项目实战

✨作者主页&#xff1a;IT毕设梦工厂✨ 个人简介&#xff1a;曾从事计算机专业培训教学&#xff0c;擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Py…

00Mac安装playwright

文章目录 前言一、执行以下命令安装二、安装如果报错zsh: command not found: pip三、安装浏览器驱动 前言 现在常用的三个自动化测试&#xff08;或者爬虫&#xff09;库&#xff0c;是Selenium、Puppeteer、Playwright。Playwright是未来趋势&#xff0c;主要学习Playwright…

资料分析笔记(花生)

preparation 资料分析首先最重要的是时间/时间段分小互换 一、速算技巧 加法技巧 1.尾数法 在多个数字精确求和或求差时&#xff0c;从“尾数”入手&#xff0c;为保证精确与速度&#xff0c;一般可观察两位。 求和题目中&#xff0c;若四个选项中后两位都不同&#xff0c;…

小琳Python课堂:深入理解Python对象:状态、创建与管理

大家好&#xff0c;这里是小琳Python课堂。今天我们继续深入理解Python对象的状态、创建与管理。让我们一起来探索这些概念&#xff0c;以帮助你更好地掌握Python编程。 一、Python对象的状态 1. 实例属性与类属性 Python对象的属性分为实例属性和类属性。实例属性是对象独有…

TRIZ在充电桩安全中的应用探究

在当今电动汽车日益普及的时代&#xff0c;充电桩的安全问题至关重要。TRIZ&#xff08;发明问题解决理论&#xff09;可以为提升充电桩的安全性提供强大助力。具体步骤如深圳天行健企业管理咨询公司下文所述&#xff1a; 一、充电桩安全面临的问题 1.电气安全风险&#xff1a…

Ansys Zemax 高级实战(HUD)开启光学新征程

亲爱的光学设计爱好者们&#xff1a; 你是否渴望在车载 HUD 设计领域大展身手&#xff1f;是否想深入了解 Ansys Zemax 的强大功能和应用技巧&#xff1f;现在&#xff0c;机会来了&#xff01; 我们将于 2024年10月24日至25日 举办为期两天的 Ansys Zemax 高级实战&#xff…

深度学习--对抗生成网络(GAN, Generative Adversarial Network)

对抗生成网络&#xff08;GAN, Generative Adversarial Network&#xff09;是一种深度学习模型&#xff0c;由Ian Goodfellow等人在2014年提出。GAN主要用于生成数据&#xff0c;通过两个神经网络相互对抗&#xff0c;来生成以假乱真的新数据。以下是对GAN的详细阐述&#xff…

【原创】java+swing+mysql简易员工管理系统设计与实现

个人主页&#xff1a;程序员杨工 个人简介&#xff1a;从事软件开发多年&#xff0c;前后端均有涉猎&#xff0c;具有丰富的开发经验 博客内容&#xff1a;全栈开发&#xff0c;分享Java、Python、Php、小程序、前后端、数据库经验和实战 文末有本人名片&#xff0c;希望和大家…

web登录校验

基础登录功能 LoginController PostMapping("/login")Result login(RequestBody Emp emp) {log.info("前端&#xff0c;发送了一个登录请求");Emp e empService.login(emp);return e!null?Result.success():Result.error("用户" "名或密…

isxdigit函数讲解 <ctype.h>头文件函数

目录 1.头文件 2.isxdigit函数使用 方源一把抓住VS2022&#xff0c;顷刻 炼化&#xff01; ​​​​​​​ 1.头文件 以上函数都需要包括头文件<ctype.h> &#xff0c;其中包括 isxdigit 函数 #include<ctype.h> 2.isxdigit函数使用 isxdigit 函数是判断字符…

Leetcode Hot 100刷题记录 -Day10(合并区间)

合并区间 问题描述&#xff1a; 以数组 intervals 表示若干个区间的集合&#xff0c;其中单个区间为 intervals[i] [starti,endi] 。请你合并所有重叠的区间&#xff0c;并返回 一个不重叠的区间数组&#xff0c;该数组需恰好覆盖输入中的所有区间 。 示例 1&#xff1a; 输入&…

vscode从本地安装插件

1. 打开VSCode。 2. 点击左侧菜单中的“扩展”&#xff08;或按CtrlShiftX&#xff09;。 3. 点击“更多操作”&#xff08;三个点&#xff09;> “从VSIX安装”。 4. 选择下载的.vsix文件。 5. 点击“安装”即可安装插件。

IstoreOS安装的1Panel无法安装应用

IstoreOS安装的1Panel无法安装应用&#xff0c;无法安装OpenResty&#xff0c;创建Docker提示文件不存在 这个路径&#xff1a; /root/Configs/1Panel/1panel/apps/openresty/openresty/www /root/Configs/1Panel/1panel/apps/openresty/openresty/1pwaf/data /root/Configs/…

请求响应-02.请求-postman工具

一.前后端分离开发 当前主流的开发模式是前后端分离开发&#xff0c;每开发一个功能&#xff0c;就需要对该功能接口进行测试&#xff0c;当前我们的测试方法是直接将url地址输入到浏览器中&#xff0c;查看web页面是否满足我们的要求。但是浏览器发起的请求全部都是GET请求&am…

【笔记】408刷题笔记

文章目录 三对角三叉树求最小带权路径UDP报文首部和TCP报文首部IP报文首部TCP报文首部UDP报文首部 刷新和再生的区别地址译码 为了区分队空队满&#xff0c;可以使用三种处理方式 1&#xff09;牺牲一个单元 队头指针在队尾指针的下一位置作为队满的标志 队满条件&#xff1a;(…

每日一题,力扣leetcode Hot100之238.除自身以外数组的乘积

乍一看这个题很简单&#xff0c;但是不能用除法&#xff0c;并且在O(N)时间复杂度完成或许有点难度。 考虑到不能用除法&#xff0c;如果我们要计算输出结果位置i的值&#xff0c;我们就要获取这个位置左边的乘积和右边的乘积&#xff0c;那么我新设立两个数组L和R。 对于L来…

Hive 本地启动时报错 Persistence Manager has been closed

Hive 本地启动时报错 Persistence Manager has been closed 2024-09-07 17:21:45 ERROR RetryingHMSHandler:215 - Retrying HMSHandler after 2000 ms (attempt 2 of 10) with error: javax.jdo.JDOFatalUserException: Persistence Manager has been closedat org.datanucle…