【C++】21年精通C++之泛型编程和模板初阶知识

❤️前言

        大家好!今天和大家一起学习关于C++泛型编程和模板初阶的相关知识。

正文

        我们之前已经学习了C++中非常重要的一个特性——函数重载,函数重载很好地提高了我们代码的可读性。但是对于适配多种参数的某种函数来说,我们如果使用函数重载就需要编写多个函数来完成不同的需要,这样的话不仅我们写代码会很累,而且写出的代码也会变得冗余。也就是说代码的复用性较差,而且有时难以维护。

        那么我们就想:是否能做到我们交给编译器一个模板,它在运行时给我们根据需求创造出具体的事物并很好地完成相应的任务呢?

        显然我们的前辈们早就想到了这一点,提前栽下了一颗树,我们只需要在其下乘凉即可,在C++中解决问题的方法就是——泛型编程

        泛型编程的含义是编写与类型无关的通用代码,它是代码复用的一种手段。模板是泛型编程的基础。其中,模板分为函数模板和类模板。

函数模板的概念与基本使用

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

        知道了函数模板的概念,我们现在来看看函数模板的基本使用方式:

template<typename T1, typename T2,......,typename Tn>
返回值类型 函数名(参数列表)
{...}

         其中,template是模板对应的关键字,typename是模板参数类型对应的关键字,只要我们在尖括号中写入了多个模板参数,我们就可以利用这些种类的模板参数对模板进行描述。具体的使用方式以交换函数Swap来展示:

// 交换任意的相同类型变量 a 、 b
template<typename T>
void Swap(T& a, T& b)
{
	T tmp = a;
	a = b;
	b = tmp;
}

        我们可以看到,T被我们用来当作一种类型来使用,如果我们进行函数调用和传参,那么这个类型就是固定的了,这时编译器就可以帮我们将这个模板转化成一个真实存在的函数,并且进行一系列的使用,这就是函数模板的基本使用方式了。

函数模板的实现原理

        根据我们上面对于函数模板的基本了解,我们大概可以得出如下的结论:函数模板是一个蓝图,它本身并不能完成函数的职能,但是它为编译器提供了模板,也就是起一个模具的作用。这种方式将我们本该做的很多重复的事情交给了编译器,减少了代码冗余,提高了写代码的效率。

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

函数模型的实例化

        用不同类型的参数使用函数模板时,称为函数模板的实例化。模板参数实例化分为隐式实例化显式实例化

隐式实例化:

        隐式实例化的意思是编译器根据实参推演模板参数的类型并生成实际函数。现在我们以上面的Swap函数来做例子详细介绍隐式实例化的使用方式:

// 在main函数中执行如下代码:
int main()
{
	int a = 0; int b = 1;
	int c = 0; double d = 1.0;
    // 正确的使用方式
	Swap(a, b);
    // 交换c、d的这条调用会失败并报错
	Swap(c, d);
	return 0;
}

        当我们像上面那样去写,我们会发现第二条代码出现了问题:

        也就是说,使用函数模板隐式实例化出的函数并不能进行对实参的隐式类型转换,不过这也很好理解,因为这里只有一个模板参数T,但是两个实参的类型并不相同,这时编译器也就无法判断它应该生成什么样的函数了。(编译器已经帮我们做了很多事了,所以我们在这里还是老老实实的按照规矩来写吧)

        要解决这个问题只有一个方式,就是手动在模板参数列表里加上一个参数,并对Swap函数进行改造:

// 将Swap函数进行改造
template<typename T1,typename T2>
void Swap(T1& t1, T2& t2)
{
	T1 tmp = t1;
	t1 = t2;
	t2 = tmp;
}

显式实例化:

        显式实例化是指在调用函数时在函数名后加上<>并在其中指定模板参数的实际类型。以Swap函数为例:

	int a = 0; int b = 1;
	Swap<int>(a, b);

模板参数的匹配原则

        我们可以对模板参数的匹配原则做一个小总结:

  1. 一个非模板函数可以和一个同名的函数模板同时存在,而且该函数模板还可以被实例化为这个非模板函数。
  2. 对于非模板函数和同名函数模板,如果其他条件都相同,在调动时会优先调用非模板函数而不会从该模板产生出一个实例。当然,如果模板可以产生一个具有更好匹配的函数,那么将选择模板。
  3. 模板函数不允许自动类型转换,但普通函数可以进行自动类型转换。

类模板的定义

        类模板的基本定义方式:

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<int> s1;
Vector<double> s2;

🍀结语

        谢谢大家的阅读,同时希望大家阅读了这篇文章之后能有所收获。祝看到这的大家能够天天开心!

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Maven安装和配置(详细版)

Maven安装和配置 Maven安装1、安装链接&#xff1a;2、配置环境变量&#xff1a; Maven配置1、修改Maven仓库下载镜像及修改仓库位置&#xff1a;2、在Idea上配置Maven&#xff1a; 测试Maven安装能否安装jar包 Maven安装 1、安装链接&#xff1a; Maven – Download Apache …

阿里云服务器 之 mqtt服务器搭建及使用

本文主要是对mqtt的学习使用&#xff0c;其中服务器是基于阿里云服务器的mqtt功能&#xff0c;客户端使用的是mqttx软件。 一、服务器部分搭建说明 1、如果是首次使用&#xff0c;则需要经过注册与认证的步骤。 2、找到"产品与服务"-->"物联网平台"&…

【MySQL】多表查询

上一篇介绍了外键约束,外键约束是用于连接两张数据表的,所以在此基础上就有了多表查询 之前的查询都是单表查询,这里我们会将多个数据表的数据结果返回在一张表上 文章目录 1.多表关系2.多表查询2.1 多表查询分类2.2 内连接2.3 外连接2.4 自连接2.5 联合查询2.6子查询 1.多表关…

微信小程序nodejs+vue+uniapp超市网上购物商城系统

超市购物系统用户端要求在系统的安卓手机上可以运行&#xff0c;主要实现了管理端&#xff1b;首页、个人中心、用户管理、商品分类管理、商品信息管理、商品入库管理、订单信息管理、订单配送管理、订单评价管理、退货申请管理、换货申请管理、系统管理&#xff0c;用户端&…

【大数据学习篇7】小试牛刀统计并且分析天猫数据

本项目基于搭建大数据环境&#xff0c;通过将数据存放在HDFS上&#xff0c;从HDFS中获取数据&#xff0c;然后根据实际需求通过Spark或Spark SQL对数据进行读取分析&#xff0c;将分析结果存储到HBase表中&#xff0c;最终通过 ECharts数据可视化工具基于Python Web平台实现数据…

docker-compose 实现Seata Server高可用部署 | Spring Cloud 51

一、前言 Seata 是一款开源的分布式事务解决方案&#xff0c;致力于提供高性能和简单易用的分布式事务服务。Seata 将为用户提供了 AT、TCC、SAGA 和 XA 事务模式&#xff0c;为用户打造一站式的分布式解决方案。 TC (Transaction Coordinator) - 事务协调者 维护全局和分支事…

看模型、做技术交底、做项目汇报,图新说数字化汇报平台引领交互式汇报新模式

现场汇报效果不好&#xff0c;导致丢了一个项目&#xff01; 项目汇报平淡无奇&#xff0c;方案屡次被毙&#xff01; 面对专家质疑&#xff0c;回答苍白无力&#xff01; 估计大家都有过这种经历和感受。 详细分析一下&#xff0c;基本上有以下几个方面的原因&#xff1a; …

虚幻or现实?堆区、栈区真实存在吗?是操作系统在骗你罢了...

文章目录 &#x1f490;专栏导读&#x1f490;文章导读&#x1f427;引例 &#x1f426;进程地址空间&#x1f426;虚拟地址与物理内存的联系&#x1f514;回答引例中的问题&#x1f513;写时拷贝 &#x1f426;虚拟地址存在的意义&#x1f513;malloc的本质 &#x1f490;专栏…

装饰者设计模式解读

问题引进 星巴克咖啡订单项目&#xff08;咖啡馆&#xff09;&#xff1a; 1) 咖啡种类/单品咖啡&#xff1a;Espresso(意大利浓咖啡)、ShortBlack、LongBlack(美式咖啡)、Decaf(无因咖啡) 2) 调料&#xff1a;Milk、Soy(豆浆)、Chocolate 3) 要求在扩展新的咖啡种类时&#x…

接口测试全流程扫盲,让我看看有哪些漏网之鱼

目录 扫盲内容&#xff1a; 1.什么是接口&#xff1f; 2.接口都有哪些类型&#xff1f; 3.接口的本质及其工作原理是什么&#xff1f; 4.什么是接口测试&#xff1f; 5.问什么要做接口测试&#xff1f; 6.怎样做接口测试&#xff1f; 7.接口测测试点是什么&#xff1f;…

一些云原生开源安全工具介绍

本博客地址&#xff1a;https://security.blog.csdn.net/article/details/130789465 一、Kubernetes安全监测工具kube-bench kube-bench是一个用Golang开发的、由Aqua Security发布的自动化Kubernetes基准测试工具&#xff0c;它运行CIS Kubernetes基准中的测试项目。这些测试…

MySQL高级篇第一天

目录 一、索引 二、索引结构 三、索引分类 四、索引语法 五、索引设计原则 六、视图 七、存储过程与概述 八、触发器 九、总结 一、索引 &#xff08;一&#xff09;索引概述 索引是一种能够帮组Mysql高效的从磁盘上查询数据的一种数据结构&#xff0c;这些数据结构以某…

用WaveNet预测(Adapted Google WaveNet-Time Series Forecasting)

目录 剧情简介: 数据来源 加载数据 分割数据和可视化 时间序列的多元波网模型:实现(多步预测) 创建模型 创建数据集 数据准备 1- Training dataset preparation 2- Validation dataset preparation Train the Model with TPU: 使用经过训练的适应Google WaveNet预测…

YOLO NAS note 1

Git Hub: https://github.com/Deci-AI/super-gradients Yolo-Nas 的代码比YOLO v8 还恐怖。之前的YOLO数据可以通过&#xff1a; coco_detection_yolo_format_train&#xff0c; 和 coco_detection_yolo_format_val 自动转。 这里写目录标题 Train数据获取数据增强训练criteri…

ChatGPT 提问,软件杂项部分

堆内存与栈内存一般分别 有多少 ChatGPT 堆内存和栈内存的大小取决于操作系统和编译器的限制以及程序的运行环境。以下是一些常见的默认大小范围&#xff0c;但请注意这些值可以因环境而异&#xff1a; 栈内存大小&#xff1a; Windows平台&#xff1a;默认情况下&#xff…

CNN实现手写数字识别(Pytorch)

CNN结构 CNN&#xff08;卷积神经网络&#xff09;主要包括卷积层、池化层和全连接层。输入数据经过多个卷积层和池化层提取图片信息后&#xff0c;最后经过若干个全连接层获得最终的输出。 CNN的实现主要包括以下步骤&#xff1a; 数据加载与预处理模型搭建定义损失函数、优…

应用现代化中的弹性伸缩

作者&#xff1a;马伟&#xff0c;青云科技容器顾问&#xff0c;云原生爱好者&#xff0c;目前专注于云原生技术&#xff0c;云原生领域技术栈涉及 Kubernetes、KubeSphere、KubeKey 等。 2019 年&#xff0c;我在给很多企业部署虚拟化&#xff0c;介绍虚拟网络和虚拟存储。 2…

微服务架构初探

大家好&#xff0c;我是易安&#xff01;我们今天来谈一谈微服务架构的前世今生。 我们先来看看维基百科是如何定义微服务的。微服务的概念最早是在2014年由Martin Fowler和James Lewis共同提出&#xff0c;他们定义了微服务是由单一应用程序构成的小服务&#xff0c;拥有自己的…

建立在Safe生态的—GameFi SocialFi双赛道项目No.1头号玩家

最近大家关注的重点在BRC-20和MEME项目&#xff0c;人们似乎更在意短期的投机回报。而在这之外&#xff0c;一个web3的游戏——No.1头号玩家却得到了大量的玩家支持。 据了解&#xff0c;No.1是一个GameFi & SocialFi的双赛道web3游戏&#xff0c;中文名称为头号玩家。它是…

光纤衰减器作用及使用说明

在光纤通信中&#xff0c;光信号的强度过大或过小都会对信号的传输和接收产生不良的影响&#xff0c;因此光衰减器在光通信系统中起到了重要的作用。那什么是光衰减器呢&#xff1f;它又有什么作用呢&#xff1f;下面跟着小易一起来了解一下吧&#xff01; 一、什么是光纤衰减…