C++模板—函数模板、类模板

 目录

一、函数模板

1、概念

2、格式

3、实例化

4、模板参数的匹配 

二、类模板

1、定义格式

2、实例化


交换两个变量的值,针对不同类型,我们可以使用函数重载实现。
void Swap(double& left, double& right)
{double tmp = left;left = right;right = tmp;
}
void Swap(int& left, int& right)
{int tmp = left;left = right;right = tmp;
}
使用函数重载虽然可以实现,但是有一下几个不好的地方:
  • 重载的函数仅仅是类型不同,代码复用率比较低,只要有新类型出现时,就需要用户自己增加对应的函数。
  • 代码的可维护性比较低,一个出错可能所有的重载均出错。

C++中提供了一种新的方式——泛型编程:编写与类型无关的通用代码,是代码复用的一种手段。模板是泛型编程的基础。

一、函数模板

1、概念

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

2、格式

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

返回值类型 函数名(参数列表){ },typename是用来定义模板参数关键字,也可以使用class(切记:不能使用struct代替class)。

交换变量值的函数使用函数模板如下:

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

简化过程的模板实际上是编译器帮我们处理了复杂的过程。

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

 

 定义多个模板数:

template<class A,class B>
void Fun{}

其实库里包含了swap函数,我们以后可以直接用。

int main()
{int a = 1, b = 2;swap(a, b);double c = 1.1, d = 2.22;swap(a, b);return 0;
}

3、实例化

函数模板的实例化是指根据函数模板创建具体的函数实例,实例化函数模板的过程是将函数模板中的类型参数替换为实际的类型,并生成对应的函数定义。这样就可以根据不同的类型参数创建多个函数实例,每个实例都可以处理相应类型的数据。

模板参数实例化分为:隐式实例化和显式实例化
template<class T>
T Add(const T& left, const T& right)
{return left + right;
}int main()
{int a1 = 10, a2 = 20;double d1 = 10.11, d2 = 20.22;Add(a1, a2);Add(d1, d2);Add(a1, d1);return 0;
}

前两个相同类型可以正常编译,但模板参数类型不同时编译出现错误。

这时因为在编译期间,当编译器看到该实例化时,需要推演其实参类型通过实参a1将T推演为int,通过实参d1将T推演为double类型,但模板参数列表中只有一个T, 编译器无法确定此处到底该将T确定为int 或者 double类型而报错,在模板中,编译器一般不会进行类型转换操作。

我们可以选择强制类型转换解决,或者显示实例化。

 方法一:显式类型转换,实参传递给形参,自动推演模板类型。

template<class T>
T Add(const T& left, const T& right)
{return left + right;
}int main()
{int a1 = 10, a2 = 20;double d1 = 10.11, d2 = 20.22;cout << Add(a1, (int)d1) << endl;//显示类型转换cout << Add((double)a1, d1) << endl;return 0;
}

 

方法二:显示实例化,在函数名和参数列表中间加上模板参数,参数隐式类型转换。

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

template<class T>
T Add(const T& left, const T& right)
{return left + right;
}int main()
{int a1 = 10, a2 = 20;double d1 = 10.11, d2 = 20.22;cout << Add<int>(a1, d1) << endl;//隐式类型转换cout << Add<double>(a1, d1) << endl;return 0;
}

4、模板参数的匹配 

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

下面这两个函数是可以同时存在的。 

int Add(int left, int right)
{return left + right;
}template<class S>
S Add(S left,S right)
{return left + right;
}int main()
{Add(1, 2); return 0;
}

 在Add函数调用处打断点,我们试着观察一下到底会调用哪个函数。

 

 可以发现,Add函数选择了专门处理int的加法函数。

 

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

我们也可以通过显示实例化调用函数模板。 

 

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

  • 在函数模板中,对于函数参数的类型推断是严格按照实参的类型进行匹配的。如果实参的类型与函数模板参数的类型不完全匹配,编译器将无法进行自动类型转换来匹配函数模板的参数类型。
  • 相比之下,普通函数可以进行自动类型转换。当调用普通函数时,如果实参的类型与函数参数的类型不完全匹配,编译器会尝试进行自动类型转换,以便匹配函数参数的类型。这种自动类型转换可以是隐式的,也可以是通过类型转换操作符进行显式的。

二、类模板

 1、定义格式

​
template<class T1, class T2, ..., class Tn>
class 类模板名
{// 类内成员定义
};
// 动态顺序表
// 注意:Vector不是具体的类,是编译器根据被实例化的类型生成具体类的模具
template<class T>
class Vector
{
public:Vector(size_t capacity = 10): _pData(new T[capacity]), _size(0), _capacity(capacity){}// 使用析构函数演示:在类中声明,在类外定义。~Vector();void PushBack(const T& data);void PopBack();// ...size_t Size() { return _size; }T& operator[](size_t pos){assert(pos < _size);return _pData[pos];}private:T* _pData;size_t _size;size_t _capacity;
};
// 注意:类模板中函数放在类外进行定义时,需要加模板参数列表
template <class T>
Vector<T>::~Vector()
{if (_pData)delete[] _pData;_size = _capacity = 0;
}​

在这段代码中,`Vector` 是一个类模板,它并不是一个具体的类,而是一个用于生成具体类的模具或蓝图。当我们使用 `Vector` 类模板时,需要提供具体的类型参数,例如 `Vector<int>` 或 `Vector<double>`,编译器会根据这些类型参数生成对应的具体类。

类模板的定义中使用了模板参数 `T`,它表示一个占位符类型,可以在实例化时被具体的类型替换。在这个例子中,`T` 表示动态顺序表中存储的元素类型。

通过实例化类模板,编译器会根据模板定义生成具体的类,其中的成员函数和成员变量的类型会被替换为实际的类型。例如,`Vector<int>` 实例化后的类将具有 `int* _pData`、`size_t _size` 和 `size_t _capacity` 成员变量,以及相应的成员函数。

因此,`Vector` 并不是一个具体的类,而是一个用于生成具体类的模板。每次使用不同的类型参数实例化 `Vector`,都会生成一个独立的具体类,用于处理特定类型的数据。这样可以提供代码的灵活性和重用性,使得我们可以使用相同的代码逻辑处理不同类型的数据。

2、实例化

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

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

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

相关文章

黑马一站制造数仓实战1

1. 项目目标 一站制造 企业中项目开发的落地&#xff1a;代码开发 代码开发&#xff1a;SQL【DSL SQL】 SparkCore SparkSQL 数仓的一些实际应用&#xff1a;分层体系、建模实现 2. 内容目标 项目业务介绍&#xff1a;背景、需求 项目技术架构&#xff1a;选型、架构 项目环境…

SpringBootWeb案例_03

Web后端开发_06 SpringBootWeb案例_03 登录认证 智能学习辅助系统登录时需要身份验证 1.登录功能 先实现简单的登录功能&#xff0c;在进一步优化。 1.1需求 若账户或密码不存在/密码不正确&#xff0c;则登录失败。 账户密码正确&#xff0c;则登录成功 1.2接口文档 …

git基本概念

一、版本控制概念 1.1 什么是版本控制 1.1.1 手动管理文件版本 1.1.2 版本控制软件 概念&#xff1a;版本控制软件是一个用来记录文件发生的变化&#xff0c;以便将来查阅特定版本修订情况的系统&#xff0c;有时也叫“版本控制系统”。通俗的理解就是把手工管理文件版本的方…

关于电脑提示vcruntime140_1.dll无法继续执行代码的解决办法

vcruntime140_1.dll是Visual C运行时库的一个组成部分&#xff0c;它包含了大量用于支持C应用程序运行时的功能。这个文件通常在开发和使用C程序时被调用&#xff0c;特别是在使用Microsoft Visual Studio进行开发时。vcruntime140_1.dll文件丢失或损坏会导致C程序无法正常运行…

信息化,数字化,智能化是3种不同概念吗?与机械化,自动化矛盾吗?

先说结论&#xff1a; 1、信息化、数字化、智能化确实是3种不同的概念&#xff01; 2、这3种概念与机械化、自动化并不矛盾&#xff0c;它们是制造业中不同发展阶段和不同层次的概念。 机械化&#xff1a;是指在生产过程中使用机械技术来辅助人工完成一些重复性、单一性、劳…

助力android面试2024【面试题合集】

转眼间&#xff0c;2023年快过完了。今年作为口罩开放的第一年大家的日子都过的十分艰难&#xff0c;那么想必找工作也不好找&#xff0c;在我们android开发这一行业非常的卷&#xff0c;在各行各业中尤为突出。android虽然不好过&#xff0c;但不能不吃饭吧。卷归卷但是还得干…

Pytorch——多卡GPU训练与单卡GPU训练相互切换

部分深度学习网络默认是多卡并行训练的&#xff0c;由于某些原因&#xff0c;有时需要指定在某单卡上训练&#xff0c;最近遇到一个&#xff0c;这里总结如下。 目录 一、多卡训练1.1 修改配置文件1.2 修改主训练文件1.3 显卡使用情况 二、单卡训练2.1 修改配置文件2.2 显卡使…

简单了解下IP的全球划分【笔记】

国际互联网号码分配机构 (The Internet Assigned Numbers Authority&#xff0c;简称IANA&#xff09;。它是互联网名称与数字地址分配机构&#xff08;The Internet Corporation for Assigned Names and Numbers&#xff0c;简称ICANN&#xff09;旗下的一个机构&#xff0c;主…

Linux5-计划任务、进程

计划任务 一、cron 计划任务 周期性计划任务 cron 任务概述 • 用途:按照设置的时间间隔为用户反复执行某一项固定的系统任务 • 软件包&#xff1a;cronie、crontabs • 系统服务&#xff1a;crond • 日志文件&#xff1a;/var/log/crond 管理计划任务策略 • 使用 cro…

存储虚拟化的写入过程

存储虚拟化的场景下&#xff0c;整个写入的过程。 在虚拟机里面&#xff0c;应用层调用 write 系统调用写入文件。write 系统调用进入虚拟机里面的内核&#xff0c;经过 VFS&#xff0c;通用块设备层&#xff0c;I/O 调度层&#xff0c;到达块设备驱动。虚拟机里面的块设备驱动…

uniapp uni-popup组件在微信小程序中滚动穿透问题

起因 在微信小程序中使用uni-popup组件时&#xff0c;出现滚动穿透&#xff0c;并且uni-popup内部内容不会滚动问题。 解决 滚动穿透 查阅官方文档&#xff0c;发现滚动穿透是由于平台差异性造成的&#xff0c;具体解决可以参照文档禁止滚动穿透 <template><page-…

【概率统计】如何理解概率密度函数及核密度估计

文章目录 概念回顾浅析概率密度函数概率值为0&#xff1f;PDF值大于1&#xff1f;一个栗子 核密度估计如何理解核密度估计核密度估计的应用 总结 概念回顾 直方图&#xff08;Histogram&#xff09;&#xff1a;直方图是最直观的一种方法&#xff0c;它通过把数据划分为若干个区…

软件工程 - 第8章 面向对象建模 - 2 静态建模

静态建模&#xff08;类和对象建模&#xff09; 类和对象模型的基本模型元素有类、对象以及它们之间的关系。系统中的类和对象模型描述了系统的静态结构&#xff0c;在UML中用类图和对象图来表示。 类图由系统中使用的类以及它们之间的关系组成。类之间的关系有关联、依赖、泛…

Numpy进阶

NumPy进阶80题完整版

卓扬网林荣雄说:开盘30分钟便能看清股票的涨跌

在一些人眼中&#xff0c;炒股是一件再简单不过的事情了&#xff0c;他们认为只需要简单地买进卖出&#xff0c;就可以玩转股票了。话虽如此&#xff0c;但是要想真正立足于股市&#xff0c;简单的买进卖出是远远不够的。要想长期屹立于股市之中&#xff0c;广大股民还需要积累…

Adobe ColdFusion文件读取漏洞(CVE-2010-2861)

任务一&#xff1a; 复现漏洞 任务二&#xff1a; 尝试利用漏洞读取目标系统中的“opt/coldfusion8/license.txt"文件 1.环境搭建&#xff08;网上写的密码是admin&#xff0c;就用admin&#xff09; 2.看答案就是一层一层进行路径穿越攻击&#xff0c;这里要注意如果…

【动态规划】LeetCode-63.不同路径II

&#x1f388;算法那些事专栏说明&#xff1a;这是一个记录刷题日常的专栏&#xff0c;每个文章标题前都会写明这道题使用的算法。专栏每日计划至少更新1道题目&#xff0c;在这立下Flag&#x1f6a9; &#x1f3e0;个人主页&#xff1a;Jammingpro &#x1f4d5;专栏链接&…

matlab diff和gradient

gradient 求解梯度。 示例 FX gradient(F) 返回向量 F 的一维数值梯度。输出 FX 对应于 ∂F/∂x&#xff0c;即 x&#xff08;水平&#xff09;方向上的差分。点之间的间距假定为 1。 使用方法&#xff1a; x -2:0.2:2; y x’; z x .* exp(-x.^2 - y.^2); [px,py] gradien…

数据库-MySQL之数据库必知必会17-21章

第17章 组 合 查 询 创建组合查询 可用UNION操作符来组合数条SQL查询。利用UNION&#xff0c;可给出多条SELECT语句&#xff0c;将它们的结果组合成单个结果集。 **例子&#xff1a;**假如需要价格小于等于5的所有物品的一个列表&#xff0c;而且还想包括供应商1001和1002生产…

【Linux】:信号(三)捕捉

信号捕捉 一.sigaction1.基本使用2.sa_mask字段 二.可重入函数三.volatile四.SIGCHLD信号 承接上文 果信号的处理动作是用户自定义函数,在信号递达时就调用这个函数,这称为捕捉信号。由于信号处理函数的代码是在用户空间的,处理过程比较复杂,举例如下: 用户程序注册了SIGQUIT信…