模版(初级)

一.泛型编程

当我们要写一个交换函数时,面对不同的类型,我们可能就需要向如下这么写:

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

那能否告诉编译器一个模子,让编译器根据不同的类型利用该模子来生成代码呢?
如果在C++中,也能够存在这样一个模具,通过给这个模具中填充不同材料(类型),来获得不同材料的铸件(即生成具体类型的代码),那将会节省许多头发。巧的是前人早已将树栽好,我们只需在此乘凉。

在此基础上我们提出了泛型编程:

定义:

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

模版我们也分为以下两类:

下面我们就分别简单介绍两类模版:

二.函数模版

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

模板格式:

template<typename T1,typename T2……typename Tn>
返回类型 + 函数名 (参数列表)
{//函数体
}

例如:

template<typename T>
void Swap( T& left, T& right)
{T temp = left;left = right;right = temp;
}

注意点:

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

这样我们会到swap上:

template<typename T>
void Swap(T& s1, T& s2)//注意:Swap要大写,swap会与库中冲突
{T temp = s1;s1 = s2;s2 = temp;
}
int main()
{int a = 10, b = 20;Swap(a, b);cout << "a=" << a << endl;cout << "b=" << b << endl;double d1 = 3.14, d2 = 4.31;Swap(d1, d2);cout << "d1=" << d1 << endl;cout << "d2=" << d2 << endl;return 0;
}

总的来说:

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

函数模板的实例化定义:
用不同类型的参数使用函数模板时,称为函数模板的实例化。

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

//隐式实例化:
template<class T>//注意:在函数模版中class和typename是一样的效果
T Add(const T& left, const T& right)
{return left + right;
}
int main()
{int a1 = 10, a2 = 20;double d1 = 10.0, d2 = 20.1;cout << Add(a1, a2) << endl;cout<<Add(d1, d2) << endl;return 0;
}

如果我们面对以下情况:

cout << Add(a1, d2) << endl;

一个是int,另一个是double,那么我们如何解决呢?

注意:在模板中,编译器一般不会进行类型转换操作,因为一旦转化出问题,编译器就需要背黑锅
 

此时有两种处理方式:

1. 用户自己来强制转化

cout << Add(a1, (int)d2) << endl;

2. 使用显式实例化
 

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

cout << Add<double>(a1, d2) << endl;

如果我们非模版与模版同时存在会调用哪个呢?

// 专门处理int的加法函数
int Add(int left, int right)
{cout << "Add(int left, int right)" << endl;return left + right;
}
// 通用加法函数
template<class T>
T Add(T left, T right)
{cout << "Add(T left, T right)" << endl;return left + right;
}
int main()
{Add(1, 2);return 0;
}

结果:

结论:

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

补充:

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

// 专门处理int的加法函数
int Add(int left, int right)
{cout << "Add(int left, int right)" << endl;return left + right;
}
// 通用加法函数
template<class T>
T Add(T left, T right)
{cout << "Add(T left, T right)" << endl;return left + right;
}
int main()
{Add<int>(1, 2);return 0;
}

结果:

除此之外,我们还要注意的是:

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

三.类模板

类模板的定义格式:

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

例如:

template <class T>
class Stack
{
public:void push(){//.....}
private:T* _a;int _top;int _capacity;
};

补充:

template <class T>
class Stack
{
public:void push(const T& x);
private:T* _a;int _top;int _capacity;
};
//注意:类模板中函数放在类外进行定义时,需要加模板参数列表
template <class T>
void Stack<T>::push(const T& x)
{}

下面我们来讲下类模版和typedef区别:

1.typedef我们可以定义自己想要的类型,但是不够便捷,而且如果我们在一份代码需要两种以上不同类型的函数/类会出现无法实现的后果

2.类模版可以解决typedef多类型问题,但是类模版如果声明和定义分离,必须在定义时加上模版,而且一般不写在多个文件(原因比较麻烦)
 

类模板实例化:

类模板实例化与函数模板实例化不同,类模板实例化需要在类模板名字后跟<>,然后将实例化的类型放在<>中即可,类模板名字不是真正的类,而实例化的结果才是真正的类
 

对比:

//函数模版实例化:
//隐式实例化:让编译器根据实参推演模板参数的实际类型
Add(a1, a2);
//显式实例化:在函数名后的<>中指定模板参数的实际类型
Add<int>(a1,d2);//类模版实例化:
// Vector类名,Vector<int>才是类型
Vector<int> s1;

感谢大家的支持,后期我们会对模版进行更加深入学习,大家现在只需要大体了解即可!!!

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【随笔】固态硬盘数据删除无法恢复(开启TRIM),注意数据备份

文章目录 一、序二、机械硬盘和固态硬盘的物理结构与工作原理2.1 机械硬盘2.11 基本结构2.12 工作原理 2.2 固态硬盘2.21 基本结构2.22 工作原理 三、机械硬盘和固态硬盘的垃圾回收机制3.1 机械硬盘GC3.2 固态硬盘GC3.3 TRIM指令开启和关闭 四、做好数据备份 一、序 周末电脑突…

HQYJ 2024-2-26 作业

1.整理链表的代码 link.stack.h文件 #ifndef __LINK_STACK_H__ #define __LINK_STACK_H__ #include<stdio.h> #include<stdlib.h> typedef int datatype; typedef struct link_stack {datatype data;struct link_stack *next;}link_stack,*link_p; typedef struc…

【Java程序设计】【C00283】基于Springboot的校园志愿者管理系统(有论文)

基于Springboot的校园志愿者管理系统&#xff08;有论文&#xff09; 项目简介项目获取开发环境项目技术运行截图 项目简介 这是一个基于Springboot的校园志愿者管理系统 本系统分为系统功能模块、管理员功能模块以及志愿者功能模块。 系统功能模块&#xff1a;用户进入到系统…

vue3.0 ref的使用

一.在vue2中定义变量 在使用vue2的时候,我们定义变量会在data中进行定义&#xff0c;那么我们在vue3中是如何定义变量的呢&#xff1f;我们会使用ref来进行定义。 (1)我们通过一个简单的案例来看 代码&#xff1a; <template> <div><button click"coun…

matlab 凸轮轮廓设计

1、内容简介 略 46-可以交流、咨询、答疑 2、内容说明 略 4 取标段的分析 取标装置是贴标机的核心部件之一&#xff0c;是影响贴标质量和贴标精度的重要因素&#xff0c;取标段是通过取标板与标签的相切运动使得涂有胶水的取标板从标签盒中粘取标签纸[4]&#xff0c;理论…

DP读书:《半导体物理学(第八版)》(一)绪论 3min速通

DP读书&#xff1a;《半导体物理学&#xff08;第八版&#xff09;》刘恩科 3min速通半导体物理之绪论 DP读书&#xff1a;《半导体物理学&#xff08;第八版&#xff09;》刘恩科绪论第一章 半导体中的电子状态1.1 半导体的晶格结构和结合性质1.1.1 金刚石型结构和共价键1.1.2…

电子科技大学课程《操作系统原理与实践》(持续更新)

前言 本人学习采用的是学校老师出版的课本&#xff0c;教学顺序与部分内容可能有所不同&#xff0c;具体以自己老师的教学为准&#xff0c;适合同学期中和期末考试的复习。重点内容用#标记&#xff0c;#数量越多&#xff0c;越重要&#xff0c;电子科技大学学生可以先看附言&a…

C++ 学习之函数对象

C 函数对象基本概念 在C中&#xff0c;函数对象&#xff08;Function Objects&#xff09;是一种类或结构体&#xff0c;它重载了函数调用运算符operator()&#xff0c;因此可以像函数一样被调用。函数对象有时也被称为仿函数&#xff08;Functor&#xff09;。 以下是关于C函…

线性表——单链表的增删查改(上)

本节复习链表的增删查改 首先&#xff0c; 链表不是连续的&#xff0c; 而是通过指针联系起来的。 如图&#xff1a; 这四个节点不是连续的内存空间&#xff0c; 但是彼此之间使用了一个指针来连接。 这就是链表。 现在我们来实现链表的增删查改。 目录 本节函数接口列表…

【大厂AI课学习笔记NO.54】2.3深度学习开发任务实例(7)数据标注和数据集拆分

数据标注 有时我们会把特征工程和数据集的标注弄混淆&#xff0c;在普通的机器学习项目中&#xff0c;我们需要进行特征工程&#xff0c;但是在深度学习项目过程中&#xff0c;我们需要进行数据标注工作。 标注工具 在本案例中&#xff0c;使用的是开源的标注工具Labelme&am…

详解三种网络适配器:HBA、NIC 和 CNA

目录 前言&#xff1a; 一、主机总线适配器 (HBA) HBA的特点 二、网络接口卡 (NIC) NIC的特点 三、并发网络适配器 (CNA) CNA的特点 四、HBA、NIC 与 CNA的区别 五、结论 前言&#xff1a; 网络中的主机总线适配器 (HBA)、网络接口卡 (NIC) 和并发网络适配器 (CNA) 是…

软件项目需求开发和管理指南

1.需求获取的方式 2.需求分析的准则 3.需求分析的方法 4.需求开发考虑的方面 5.需求确认的方法 6.需求优先级的设定 7.需求文档编制规范要求 软件全文档获取&#xff1a;软件项目开发全套文档下载_软件项目文档-CSDN博客