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.rhkb.cn/news/207580.html

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

相关文章

AI为基,快手新商业图景浮现

监制 | 何玺 排版 | 叶媛 快手新商业图景浮现&#xff01; 11月21日&#xff0c;快手发布了2023年Q3财报。该季度内&#xff0c;快手以超2成营收增长的亮眼业绩&#xff0c;展示出强大的经营韧性。同时其在付费短剧、AI应用等业务上的拓展&#xff0c;则让行业和资本市场看到…

unity学习笔记13

一、常用物理关节 Unity中的物理关节&#xff08;Physics Joints&#xff09;是用于在游戏中模拟和控制物体之间的连接。物理关节允许你在对象之间应用各种约束&#xff0c;例如旋转、移动或固定连接&#xff0c;以模拟真实世界中的物理交互。 物理关节类型&#xff1a; 1.F…

Ubuntu新手使用教程

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f984; 博客首页——&#x1f405;&#x1f43e;猫头虎的博客&#x1f390; &#x1f433; 《面试题大全专栏》 &#x1f995; 文章图文…

JavaScript类型判断:解密变量真实身份的神奇技巧

文章目录 1. typeof运算符2. instanceof运算符3. Object.prototype.toString4. Array.isArray5. 使用constructor属性6. 使用Symbol.toStringTag7. 使用is类型判断库8. 谨慎使用隐式类型转换结语 &#x1f389;JavaScript类型判断&#xff1a;解密变量真实身份的神奇技巧 ☆* o…

LeetCode105.从前序和中序遍历序列构造二叉树

这道题看完题想了几分钟就想到大概的思路了&#xff0c;但是在写的时候有很多细节没注意出了很多问题&#xff0c;然后写了1个多小时&#xff0c;其实这道题挺简单的。 首先&#xff0c;最基本的知识&#xff0c;先序遍历是根左右&#xff0c;中序遍历是左根右&#xff0c;那么…

Unittest(1):unittest单元测试框架简介setup前置初始化和teardown后置操作

unittest单元测试框架简介 unittest是python内置的单元测试框架&#xff0c;具备编写用例、组 织用例、执行用例、功能&#xff0c;可以结合selenium进行UI自动化测 试&#xff0c;也可以结合appium、requests等模块做其它自动化测试 官方文档&#xff1a;https://docs.pytho…

【ChatGLM3-6B】Docker下部署及微调

【ChatGLM2-6B】小白入门及Docker下部署 注意&#xff1a;Docker基于镜像中网盘上上传的有已经做好的镜像&#xff0c;想要便捷使用的可以直接从Docker基于镜像安装看Docker从0安装前提下载启动访问 Docker基于镜像安装容器打包操作&#xff08;生成镜像时使用的命令&#xff0…

C++11线程以及线程同步

C11中提供的线程类std::thread,基于此类创建一个新的线程相对简单&#xff0c;只需要提供线程函数和线程对象即可 一.命名空间 this_thread C11 添加一个关于线程的命名空间std::this_pthread ,此命名空间中提供四个公共的成员函数&#xff1b; 1.1 get_id() 调用命名空间s…

电商营销场景的RocketMQ实战01-RocketMQ原理

架构图 Broker主从架构与集群模式 RocketMQ原理深入剖析 Broker主从架构原理 HAConnection与HAClient Broker基于raft协议的主从架构 Consumer运行原理 基础知识 001_RocketMQ架构设计与运行流程分析 RocketMQ这一块&#xff0c;非常关键的一个重要的技术&#xff0c;面试的时候…

键盘打字盲打练习系列之指法练习——2

一.欢迎来到我的酒馆 盲打&#xff0c;指法练习&#xff01; 目录 一.欢迎来到我的酒馆二.开始练习 二.开始练习 前面一个章节简单地介绍了基准键位、字母键位和数字符号键位指法&#xff0c;在这个章节详细介绍指法。有了前面的章节的基础练习&#xff0c;相信大家对盲打也有了…

设计模式-结构型模式之适配器设计模式

文章目录 一、结构型设计模式二、适配器模式 一、结构型设计模式 这篇文章我们来讲解下结构型设计模式&#xff0c;结构型设计模式&#xff0c;主要处理类或对象的组合关系&#xff0c;为如何设计类以形成更大的结构提供指南。 结构型设计模式包括&#xff1a;适配器模式&…

Day04:每日一题:2661. 找出叠涂元素

2661. 找出叠涂元素 给你一个下标从 0 开始的整数数组 arr 和一个 m x n 的整数 矩阵 mat 。 arr 和 mat 都包含范围 [1&#xff0c;m * n] 内的 所有 整数。从下标 0 开始遍历 arr 中的每个下标 i &#xff0c;并将包含整数 arr[i] 的 mat 单元格涂色。请你找出 arr 中在 mat…

linux 磁盘管理、分区管理常用命令

文章目录 基础命令挂载新硬盘/分区添加内存交换分区swaplvm分区管理模式 基础命令 查看目录文件大小 du -sh /backup du -sh /backup/* du -sh *查看磁盘挂载信息 df -lhT查看某个目录挂载在哪个分区&#xff0c;以及分区的磁盘使用情况 df [目录] #例如&#xff1a;df /ho…

Scrapy爬虫异步框架之持久化存储(一篇文章齐全)

1、Scrapy框架初识&#xff08;点击前往查阅&#xff09; 2、Scrapy框架持久化存储 3、Scrapy框架内置管道&#xff08;点击前往查阅&#xff09; 4、Scrapy框架中间件&#xff08;点击前往查阅&#xff09; 5、Scrapy框架全站、分布式、增量式爬虫 Scrapy 是一个开源的、…

对于Web标准以及W3C的理解、对viewport的理解、xhtml和html有什么区别?

1、对于Web标准以及W3C的理解 Web标准 Web标准简单来说可以分为结构、表现、行为。 其中结构是由HTML各种标签组成&#xff0c;简单来说就是body里面写入标签是为了页面的结构。 表现指的是CSS层叠样式表&#xff0c;通过CSS可以让我们的页面结构标签更具美感。 行为指的是…

【Python表白系列】这个情人节送她一个漂浮的爱心吧(完整代码)

文章目录 漂浮的爱心环境需求完整代码详细分析系列文章 漂浮的爱心 环境需求 python3.11.4PyCharm Community Edition 2023.2.5pyinstaller6.2.0&#xff08;可选&#xff0c;这个库用于打包&#xff0c;使程序没有python环境也可以运行&#xff0c;如果想发给好朋友的话需要这…

c/c++概念辨析-指针常量常量指针、指针函数函数指针、指针数组数组指针

概念澄清&#xff1a; 统一规则&#xff1a; 不管是XX指针&#xff0c;还是指针XX&#xff0c;后者是本体&#xff0c;前者只是个定语&#xff0c;前者也可以替换为其他同类&#xff08;例如字符串&#xff09;&#xff0c;帮助理解。 XX指针&#xff1a; 可简单理解为&#…

基于helm的方式在k8s集群中部署gitlab - 部署(一)

文章目录 1. 背景说明2. 你可以学到什么&#xff1f;3. 前置条件4. 安装docker服务&#xff08;所有节点&#xff09;5. 部署k8s集群5.1 系统配置&#xff08;所有节点&#xff09;5.2 安装kubelet组件(所有节点)5.2.1 编写kubelet源5.2.2 安装kubelet5.2.3 启动kubelet 5.3 集…

从零开始,用Docker-compose打造SkyWalking、Elasticsearch和Spring Cloud的完美融合

&#x1f38f;&#xff1a;你只管努力&#xff0c;剩下的交给时间 &#x1f3e0; &#xff1a;小破站 "从零开始&#xff0c;用Docker-compose打造SkyWalking、Elasticsearch和Spring Cloud的完美融合 前言准备工作编写docker-compose.yml文件为什么使用本机ip为什么skywa…

python中的字符串

字符串 字符串是编程语言中的一种基本数据类型&#xff0c;用于表示一串字符序列。在Python中&#xff0c;字符串是不可变的&#xff0c;也就是说一旦字符串被创建&#xff0c;就无法修改其中的字符。 Python中的字符串可以用单引号或双引号括起来&#xff0c;例如&#xff1…