初识模版(C++)

初识模版(C++)

在这里插入图片描述

模版是C++的一个重大发明,是让C++突飞猛进的原因之一。

  1. 泛型编程

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

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;
}int main()
{return 0;
}

可以看到以前我们要交换不同数据类型的变量时,需要调用各自不同类型的交换函数,虽然可以重载,但还是写了很多个Swap。

我们很难不想将它们合为一个通用的Swap,对吧?

就像活字印刷的模具,我们能否有一个通用的Swap函数,别人想要交换什么类型,就自动生成为那个类型的Swap函数呢?

//模版类型
template<class T>
void Swap(T& x, T& y)//以前这里是写具体类型,现在写的是模版类型
{T tmp = x;x = y;y = tmp;
}int main()
{int i = 1, j = 2;double m = 1.1, n = 2.2;Swap(i, j);Swap(m, n);return 0;
}

其实就是让编译器去具体写我们想要类型的函数。

而且当我们用调试时观察可以发现,调用Swap时确实走到void Swap(T& x, T& y)里面去了。

然而事实上调用的不是同一个函数。IDE做了特殊处理,这样是为了方便调试看。

如果我们再深究一下,去看反汇编,就会发现调用的确实不是同一个函数:

这两个函数我们是没有写的,所以是编译器帮助我们生成的。模版的特点就是让C++在有些地方能够半自动化。

生成具体类型函数这个活是免不了的,只是由我们做变成编译器去做了。

  1. 函数模板

模版有两类,函数模版和类模版,我们刚才写的就叫做函数模版。

函数模板概念

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

函数模板格式

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

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

这里的typename关键字是后期增加的,也可以用class。typename,类型名称,会更形象一点。

模版参数列表可以类比着函数参数列表去学习,它们的规则是类似的。

函数参数列表是()圆括号,模版参数列表是<>尖括号。

函数的参数列表定义的是变量或者说对象,(C++不是纯面向对象的因为C++兼容C语言,也可以走面向过程这一套。C++中变量喜欢叫对象,其实像C一样叫变量也没什么问题。)

(类型 变量1,类型 变量2)

而模版的参数列表定义的是关键字

<class 类型1,class 类型2>

class后面不是变量名称了,而是类型。

多个类型:

template<typename T1, typename T2>
void func(const T1& x,const T2& y)
{}int main()
{int i = 1, j = 2;double m = 1.1, n = 2.2;func(i, m);func(i, j);return 0;
}

要注意的一点是,在前一个:

template<class T>
void Swap(T& x, T& y)

单类型的函数中,这个T到底是什么类型是由我们传递的实参推理决定的,所以如果我们在实参中传递了两个不同类型的值就会产生歧义,不知道该生成哪个类型的Swap函数:

template<class T>
void Swap(T& x, T& y)
{T tmp = x;x = y;y = tmp;	
}int main()
{int i = 1, j = 2;double m = 1.1, n = 2.2;Swap(i,m);//这是不行的return 0;
}

从此以后,我们就可以告辞手写Swap的时代了。C++11下这个函数在头文件中,一般会被简介包含从,所以不需要我们自己包含这个头文件。

是小写字母s开头。

int main()
{int i = 1, j = 2;double m = 1.1, n = 2.2;swap(i, j);swap(m,n);return 0;
}

以后直接使用即可。

现在我们怎么解决想要传两个不同类型的参数的问题?

第一种办法,强制类型转换

int main()
{int a1 = 10, a2 = 20;double d1 = 10.0, d2 = 20.0;cout<<Add(a1, (int)d1)<<endl;cout<<Add((double)a1, d1)<<endl;
}

本质解决的是推导冲突的问题。

还有一种方法:显式实例化

//用函数模版生成对应的函数——模版的实例化
template<class T>
T Add(const T& left, const T& right)
{return left + right;
}int main()
{int a1 = 10, a2 = 20;double d1 = 10.0, d2 = 20.0;//推导实例化cout<<Add(a1, (int)d1)<<endl;cout<<Add((double)a1, d1)<<endl;//显式实例化cout << Add<int>(a1, d1) << endl;
}

在函数名和参数直接加尖括号写类型,不通过推导而是通过显式实例化。这样我们的d1可以传给生成好的int类型Swap是因为可以走隐式类型转换。

当然也可以选择写一个两个类型的Swap。

有些情况下,必须走显式实例化。不能走自动推导:

template<class T>
T* func1(int n)
{return new T[n];
}int main()
{func1(10);return 0;
}

这样的形参无法推导出T是什么类型。

所以必须显式实例化。double* p1= func1<double>(10);

如果一个模版和一个具体类型的函数同时存在,会优先走对应类型的函数。

int a1=10,a2=20;
cout<<Add(a1,a2)<<endl;

会优先去调用第二个类型直接匹配的Add函数。

  1. 类模板

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

这里的class也能用typename替代。

// 类模版
template<typename T>
class Stack
{
public:Stack(int n = 4):_array(new T[n])//不用检查失败,不会返回空,_size(0),_capacity(n){}~Stack(){delete[] _array;_array = nullptr;_size = _capacity = 0;}void Push(const T& data)//用传值传参要拷贝,代价太大,加了引用尽量加 const{if (_size == _capacity)//满了{//没有renew这种东西,也不能用reallocT* tmp = new T[_capacity * 2];memcpy(tmp, _array, sizeof(T) * _size);delete[] _array;_array = tmp;_capacity *= 2;}_array[_size++] = x;}//……private:T* _array;size_t _capacity;size_t _size;
};

记得我们以前使用typedef来写栈的,也可以修改栈存放的数据类型,但是为什么现在要用类模版来实现呢?有什么区别呢?

int main()
{Stack st1;//intStack st2;//doublereturn 0;
}

用typedef,如果我们想要一个栈存放int类型,一个存放double类型,这是做不到的。得写两份一个存int,一个存double。

同时我们也可以发现,类模板无法推导实例化,只能显式实例化,只有函数才有推导实例化。

int main()
{Stack<int> st1;st1.Push(1);st1.Push(2);st1.Push(3);Stack<double> st2;st2.Push(1.1);st2.Push(1.1);st2.Push(1.1);return 0;
}

从此我们的栈可以不止存一类数据了。

这两个栈其实不是同一个类型,和之前的函数模版一样,也是“活字印刷”。本质上生成了两个类型的栈类型,只是编译器做了这件事。

如果我们现在想要将类成员函数的声明与定义分离呢?
像以前一样指定类域,不行。

我们定义的模版参数只能给当前的函数或类去用,每个函数模版都得定义自己的模版参数,不一定叫T。

所以出了类,T就不能用了。得在分离出来的定义前面重新定义模版参数:

//类外
template<class T>
void Stack<T>::Push(const T& data)//用传值传参要拷贝,代价太大,加了引用尽量加 const
{if (_size == _capacity)//满了{//没有renew这种东西,也不能用reallocT* tmp = new T[_capacity * 2];memcpy(tmp, _array, sizeof(T) * _size);delete[] _array;_array = tmp;_capacity *= 2;}_array[_size++] = x;
}

其次要注意的是,模版不支持声明和定义分离到两个文件。会报链接错误。以后再说原因。也有办法分离但是非常麻烦,一般模版放在一个文件。

如果我们将函数定义里的T换为X,不会报错:

为什么?因为这个本来就只是个代号,其实实际在调用Push这个函数时就已经没有这个T或者X的概念了,而是实例化为对应的类型了。调用的也不是模版的这个函数,而是生成后的函数。

这些只是模版的初阶用法,以后再进阶。

本文到此结束=_=

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

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

相关文章

DockerHub解决镜像拉取之困

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:Linux运维老纪的首页…

从零开始搭建Aliyun ESC高可用集群 (HaVip+KeepAlived)

从零开始搭建Aliyun ESC高可用集群 (HaVip+KeepAlived) 架构 架构 本设计方案采用两台阿里云ECS服务器搭建Keepalived结合LVS的高可用集群。使用LVS的TUN模式进行负载均衡,同时利用阿里云的弹性IP(EIP)与高可用虚拟HaVIP实现跨服务器的高可用性。架构中,一台ECS服务器作为…

DFS 算法:记忆化搜索

我的个人主页 {\large \mathsf{{\color{Red} 我的个人主页} } } 我的个人主页 往 {\color{Red} {\Huge 往} } 往 期 {\color{Green} {\Huge 期} } 期 文 {\color{Blue} {\Huge 文} } 文 章 {\color{Orange} {\Huge 章}} 章 无 此系列更新频繁&#xff0c;求各位读者点赞 关…

备考计算机二级Python之Day5

第5章 函数和代码 一、函数的基本使用 函数是一段具有特定功能的、可重用的语句组&#xff0c;通过函数名来表示和调用。 函数的使用包括两部分&#xff1a;函数的定义和函数的使用 1、函数的定义 Python语言通过保留字def定义函数&#xff0c;语法形式如下&#xff1a; …

SpringBoot教程(二十四) | SpringBoot集成AOP实现日志记录

SpringBoot教程&#xff08;二十四&#xff09; | SpringBoot集成AOP实现日志记录 &#xff08;一&#xff09;AOP 概要1. 什么是 AOP &#xff1f;2. 为什么要用 AOP&#xff1f;3. AOP一般用来干什么&#xff1f;4. AOP 的核心概念 &#xff08;二&#xff09;Spring AOP1. 简…

CSS3页面布局-三栏-中栏流动布局

三栏-中栏流动布局 用负外边距实现 实现三栏布局且中栏内容区不固定的核心问题就是处理右栏的定位&#xff0c; 并在中栏内容区大小改变时控制右栏与布局的关系。 控制两个外包装容器的外边距&#xff0c;一个包围三栏&#xff0c;一个包围左栏和中栏。 <!DOCTYPE html&…

vllm 部署GLM4模型进行 Zero-Shot 文本分类实验,让大模型给出分类原因,准确率可提高6%

文章目录 简介数据集实验设置数据集转换模型推理评估 简介 本文记录了使用 vllm 部署 GLM4-9B-Chat 模型进行 Zero-Shot 文本分类的实验过程与结果。通过对 AG_News 数据集的测试&#xff0c;研究发现大模型在直接进行分类时的准确率为 77%。然而&#xff0c;让模型给出分类原…

【软件测试面试题】WEB功能测试(持续更新)

Hi&#xff0c;大家好&#xff0c;我是小码哥。最近很多朋友都在说今年的互联网行情不好&#xff0c;面试很难&#xff0c;不知道怎么复习&#xff0c;我最近总结了一份在软件测试面试中比较常见的WEB功能测试面试面试题合集&#xff0c;希望对大家有帮助。 建议点赞收藏再阅读…

AI学习记录 - 怎么理解 torch 的 nn.Conv2d

有用就点个赞 怎么理解 nn.Conv2d 参数 conv_layer nn.Conv2d(in_channels1, out_channels 10 // 2, kernel_size3, stride2, padding0, biasFalse) in_channels in_channels 可以设置成1&#xff0c;2&#xff0c;3&#xff0c;4等等都可以&#xff0c;一般来说做图像识别…

微服务案例搭建

目录 一、案例搭建 1.数据库表 2.服务模块 二、具体代码实现如下&#xff1a; (1) 首先是大体框架为&#xff1a; &#xff08;2&#xff09;父模块中的pom文件配置 &#xff08;3&#xff09;shop_common模块&#xff0c;这个模块里面只需要配置pom.xml&#xff0c;与实体…

MySQL如何判断一个字段里面是否包含汉字

SQL查询中&#xff0c;length() 和 char_length() 都是用来获取字符串长度的函数 在单字节字符集下&#xff08;如ASCII&#xff09;&#xff1a;每个字符通常占用1个字节&#xff0c;因此length()和char_length()在这类字符集中给出的结果是一样 在多字节字符集下&#xff0…

matplotlib绘制子图以及局部放大效果

需求&#xff1a;绘制1*2的子图&#xff0c;子图1显示两个三角函数&#xff0c;子图2显示三个对数函数&#xff0c;子图2中对指定的区域进行放大。 绘图细节&#xff1a; 每个子图中每个函数的数据存放到一个列表中&#xff0c;然后将每个子图的数据统一存到一个列表中&#…

Go 使用Redis安装、实例和基本操作

Go使用Redis&#xff1a;详解go-redis/v9库 引言 Redis作为一个高性能的键值对数据库&#xff0c;广泛应用于缓存、消息队列、实时数据分析等场景。在Go语言中&#xff0c;go-redis/v9库提供了丰富的接口和高效的数据交互能力&#xff0c;使得在Go项目中集成Redis变得简单而高…

接口限流经典算法

文章目录 限流基于计数器的限流基于滑动窗口的限流桶漏斗算法令牌桶算法 限流 为了保证系统的安全性和稳定性&#xff0c;防止恶意流量和突发大量流量短时间内大量请求接口&#xff0c;造成服务器崩溃&#xff0c;接口的限流是有必要的。 以下是四种经典的限流算法。 基于计数…

Python测试框架Pytest的使用

pytest基础功能 pytset功能及使用示例1.assert断言2.参数化3.运行参数4.生成测试报告5.获取帮助6.控制用例的执行7.多进程运行用例8.通过标记表达式执行用例9.重新运行失败的用例10.setup和teardown函数 pytset功能及使用示例 1.assert断言 借助python的运算符号和关键字实现不…

UE5打包iOS运行查看Crash日志

1、查看Crash 1、通过xCode打开设备 2、选择APP打开最近的日志 3、选择崩溃时间点对应的日志 4、选择对应的工程打开 5、就能看到对应的Crash日志 2、为了防止Crash写代码需要注意 1、UObject在RemoveFromRoot之前先判断是否Root if (SelectedImage && Selecte…

Frog4Shell — FritzFrog 僵尸网络将一日攻击纳入其武器库

FritzFrog 的背景 Akamai 通过我们的全球传感器网络持续监控威胁,包括我们之前发现的威胁。其中包括FritzFrog 僵尸网络(最初于 2020 年发现),这是一个基于 Golang 的复杂点对点僵尸网络,经过编译可同时支持基于 AMD 和 ARM 的机器。该恶意软件得到积极维护,多年来通过增…

百日筑基第六十天-学习一下Tomcat

百日筑基第六十天-学习一下Tomcat 一、Tomcat 顶层架构 Tomcat 中最顶层的容器是 Server&#xff0c;代表着整个服务器&#xff0c;从上图中可以看出&#xff0c;一个 Server可以包含至少一个 Service&#xff0c;用于具体提供服务。Service 主要包含两个部分&#xff1a;Conn…

AI周报(8.18-8.24)

AI应用-XGO-Rider: 全球首款轮腿式桌面 AI 机器人 中国的 Luwu 智能打造的XGO-Rider 是全球首款轮腿式桌面 AI 机器人。这个小巧紧凑的机器人将轮式机器人的灵活性与腿式机器人的障碍处理能力相结合&#xff0c;可以全方位移动&#xff0c;轻松适应各种地形。 XGO-Rider 主要设…

服务商模式实现JSAPI小程序微信支付(javaphp)

官方文档 https://pay.weixin.qq.com/wiki/doc/apiv3_partner/open/pay/chapter2_1.shtml 使用wechatpay-php实现JSAPI支付&#xff08;服务商和普通商户&#xff09;文章浏览阅读1.3k次&#xff0c;点赞3次&#xff0c;收藏7次。之前我使用的sdk是“wechatpay-guzzle-middle…