【C++】C++泛型编程 | 模板初阶

news2024/11/17 9:43:16

在这里插入图片描述


  •   🧑‍🎓个人主页:简 料

  •   🏆所属专栏:C++

  •   🏆个人社区:越努力越幸运社区

  •   🏆简       介:简料简料,简单有料~在校大学生一枚,专注C/C++/GO的干货分享,立志成为您的好帮手 ~


C/C++学习路线 (点击解锁)
❤️C语言阶段(已结束)
❤️数据结构与算法(ing)
❤️C++(ing)
❤️Linux系统与网络(队列中…)

文章目录

  • ☑️前言
  • 🧑‍🎓泛型编程
  • 🧑‍🎓函数模板
    • 1. 函数模板概念
    • 2. 函数模板格式
    • 3. 函数模板原理
    • 4. 函数模板的实例化
    • 5. 模板参数的匹配原则
  • 🧑‍🎓类模板
    • 1. 类模板的定义格式
    • 2. 类模板的实例化
  • ☑️写在最后


☑️前言

🌀 本章给大家带来的是C++泛型编程—模板C++C语言的扩展,注重面向对象编程及泛型编程。泛型编程是一种基于参数化的编程方式,简单来说就是编写具有通用性,可以适用于不同类型数据的代码。模板就是C++中实现泛型编程的一种重要手段。
🌀 C++中的模板允许程序员编写一套泛型代码,可以适用于不同的数据类型,因此称之为“模板”。模板可以分为函数模板和类模板两种形式。函数模板可以让程序员编写一个通用的函数(函数的参数或返回值是泛型类型),可以适用于各种不同的数据类型。类模板可以让程序员定义一个通用的类(类的数据成员或成员函数是泛型类型),可以适用于各种不同的数据类型。
🌀 使用模板可以让程序员编写简洁、清晰、高效的代码,避免了因为重复的代码而产生的维护和修改的麻烦,提高了代码的复用性和可读性。因此,C++中的模板是一种非常有用的编程工具,在现代C++编程中被广泛应用。
🌀 所以,模板是很重要的噢~


接下来就开始本章知识的学习吧!


🧑‍🎓泛型编程

泛型编程是一种编程范式,它的核心思想是将程序中的数据类型抽象化,从而实现可以适用于多种数据类型的代码重用。

这里我们用这样一个简单的题目来引出对泛型的认识:

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

如下:

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++中,也能够存在这样一个模具,通过给这个模具中填充不同材料(类型),来获得不同材料的铸件(即生成具体类型的代码),那将会节省许多头发。巧的是前人早已将树栽好,我们只需在此乘凉。

【泛型编程】:编写与类型无关的通用代码,是代码复用的一种手段。模板是泛型编程的基础。而模板又分为函数模板和类模板。

在这里插入图片描述


🧑‍🎓函数模板

1. 函数模板概念

  1. 函数模板代表了一个函数家族,该函数模板与类型无关,在使用时被参数化,根据实参类型产生函数的特定类型版本。
  2. 函数模板是一种通用的函数定义,可以用来创建多个功能类似但参数类型不同的函数实例。在函数模板中,函数的参数类型和返回值类型不需要指定具体的类型,而是使用一个或多个类型参数来表示。

2. 函数模板格式

  • 函数模板的格式如下:
template<typename T>
返回类型 函数名(参数列表){
    // 函数体
}

其中,template<typename T> 表示定义了一个模板,<typename T> 表示定义了一个名为 T 的类型参数,可以在函数体中使用这个类型参数来声明变量类型等。返回类型 表示这个函数的返回值类型,函数名 表示函数的名称,参数列表 表示函数的参数列表。

举个例子,下面是一个简单的函数模板:

template<typename T>
T max(T a, T b){
    return a > b ? a : b;
}

这个函数模板可以比较两个同类型的变量,返回其中的较大值。使用时,可以将不同类型的变量传递给这个函数模板,因为在调用时编译器会根据实际参数的类型实例化出相应的函数版本。

  • 当然,函数模板的格式也可以这样写:
template<class T>
返回类型 函数名(参数列表){
    // 函数体
}

可以用class代替typename,效果是一样的~


3. 函数模板原理

【原理】:C++ 编译器会根据函数模板在编译期间生成多个函数版本,这些函数版本分别对应不同的传入类型,也就是说,函数模板本质上是代码生成的一种方式,它会根据模板参数类型动态生成不同的函数定义。

⭐例如,给定一个函数模板:

template<typename T>
T max(T a, T b){
    return a > b ? a : b;
}

当编译器看到这个模板的时候,它并不会立即生成一个函数定义,而是把这个模板存储在编译器的内部,直到遇到一个函数调用时,编译器才实例化这个模板,生成一个具体的函数定义。例如,当我们调用 max(3, 5) 时,编译器会实例化一个 max(int a, int b) 函数,把 ab 的类型都指定为 int,然后再把这个函数定义插入到程序中。同样的,当我们调用 max(3.5, 1.2) 时,编译器会实例化一个 max(double a, double b) 函数,把 ab 的类型都指定为 double,然后再把这个函数定义插入到程序中。这是函数模板的运行机制。

这个机制也说明了函数模板是一个蓝图,重点在于它本身并不是函数,而是编译器用使用方式产生特定具体类型的函数的模具。所以模板其实就是将本来应该我们做的重复的事情交给了编译器。因此马云的那句话说的没错:世界是懒人创造的。

⭐再给定一个函数模板:

template<class T>
void Swap(T& a, T& b)
{
 	int temp = a;
 	a = b;
 	b = temp;
}

其推演函数实例的图解如下:

在这里插入图片描述

这样看,是不是对模板的理解更为深刻了呢~


4. 函数模板的实例化

函数模板的实例化是指编译器根据函数模板和实际参数类型生成一个具体的函数的过程。模板参数实例化分为:隐式实例化显式实例化

⭐隐式实例化:让编译器根据实参推演模板参数的实际类型~

例如:

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;
 	Add(a1, a2);
 	Add(d1, d2);
 
 	/*
 	该语句不能通过编译,因为在编译期间,当编译器看到该实例化时,需要推演其实参类型
 	通过实参a1将T推演为int,通过实参d1将T推演为double类型,但模板参数列表中只有一个T,
 	编译器无法确定此处到底该将T确定为int 或者 double类型而报错
 	注意:在模板中,编译器一般不会进行类型转换操作,因为一旦转化出问题,编译器就需要背黑锅
 	Add(a1, d1);
 	*/
 
 	// 此时有两种处理方式:1. 用户自己来强制转化 2. 使用显式实例化
 	Add(a, (int)d);
 	
  	return 0;
}

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

例如:

template<class T>
T Add(const T& left, const T& right)
{
 	return left + right;
}

int main()
{
 	int a = 10;
 	double b = 20.0;
 
 	// 显式实例化
 	Add<int>(a, b);
 	
 	return 0;
}

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


5. 模板参数的匹配原则

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

例如:

// 专门处理int的加法函数
int Add(int left, int right)
{
 	return left + right;
}
// 通用加法函数
template<class T>
T Add(T left, T right)
{
 	return left + right;
}
void Test()
{
 	Add(1, 2); // 与非模板函数匹配,编译器不需要特化
 	Add<int>(1, 2); // 调用编译器特化的Add版本
}
  1. 对于非模板函数和同名函数模板,如果其他条件都相同,在调动时会优先调用非模板函数而不会从该模板产生出一个实例。如果模板可以产生一个具有更好匹配的函数, 那么将选择模板。

例如:

// 专门处理int的加法函数
int Add(int left, int right)
{
 	return left + right;
}

// 通用加法函数
template<class T1, class T2>
T1 Add(T1 left, T2 right)
{
 	return left + right;
}

void Test()
{
 	Add(1, 2); // 与非函数模板类型完全匹配,不需要函数模板实例化
 	Add(1, 2.0); // 模板函数可以生成更加匹配的版本,编译器根据实参生成更加匹配的Add函数
}
  1. 模板函数不允许自动类型转换,但普通函数可以进行自动类型转换。

🧑‍🎓类模板

1. 类模板的定义格式

类模板和函数模板的定义格式有些类似,但是在关键字和参数的使用上有一些区别。类模板的定义格式如下:

template <typename T>
class 类名 {
    // 成员变量和成员函数的定义
}

其中,typename关键字也可以替换为class关键字,表示声明一个模板类型参数。T是一个占位符,表示在使用类模板时需要传入的实际类型参数。在类模板中可以使用T作为成员变量的类型和成员函数的参数类型。

需要注意的是,在定义类模板时,只是声明了一个模板类,还没有实际创建一个可以使用的类。当需要使用模板类时,需要进行实例化操作。也就是根据需要传入的类型参数,生成特定的类。


2. 类模板的实例化

类模板的实例化是指根据传入的模板实参,生成特定的类的过程。实例化类模板的方法有两种:隐式实例化和显式实例化。

⭐【隐式实例化】是指在使用模板类时,自动推导模板实参的过程。例如下面的代码:

template <typename T>
class MyClass {
    // ...
};

MyClass<int> obj; // 隐式实例化

在实例化过程中,将类模板中的T替换为int,形成特定的类型MyClass<int>

⭐【显式实例化】是指手动进行实例化操作。需要在类模板声明的外部显式地声明一个实例化的类。例如下面的代码:

template <typename T>
class MyClass {
    // ...
};

template class MyClass<int>; // 显式实例化

在实例化过程中,将类模板中的T替换为int,形成特定的类型MyClass<int>

需要注意的是,在类模板中可以有成员函数和静态成员变量的定义,因此,在实例化过程中,这些成员也会一并进行实例化

最后需要注意一点,类模板的实例化与函数模板的实例化不同,类模板实例化的时间是在编译时期,而函数模板的实例化是在调用时期。因此,在类模板中,无法推导出模板实参类型的操作将会导致编译错误。


☑️写在最后

💝本章主要讲解的是C++泛型编程—模板。模板在我们后面C++的学习当中将会持续的出现,尤其是在STL阶段,STL将泛型编程思想体现的淋漓尽致。所以,我们不仅要理解泛型编程思想,还要能够熟练的使用模板~!再看标题,本章只是模板的初阶,虽然是初阶,但在平常的写代码中已经够用。之后,简料将会先推出一些STL的文章,而进阶将会在讲解完这些STL后推出!😊


❤️‍🔥后续将会继续输出有关C++的文章,你们的支持就是我写作的最大动力!

感谢阅读本小白的博客,错误的地方请严厉指出噢~

请添加图片描述

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

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

相关文章

LT8711UXE1,Type-C/DP1.2 转 HDMI2.0,内置HDCP,支持4k 60Hz向下兼容

1. 描述 LT8711UXE1 是一款高性能 Type-C/DP1.2 至 HDMI2.0 转换器&#xff0c;设计用于将 USB Type-C 源或 DP1.2 源连接到 HDMI2.0 接收器。LT8711UXE1 集成了一个符合 DP1.2 标准的接收器和一个符合 HDMI2.0 标准的发射器。此外&#xff0c;还包括两个用于 CC 通信的 CC 控…

研发/IT工程师双视角测评8大仿真平台,结果……

上一篇&#xff0c;我们围绕着亿万研发工程师的梦想&#xff0c;考察了市面上几乎所有类型的工业仿真平台&#xff0c;深度评测了他们本质究竟是什么&#xff0c;适用场景有哪些&#xff0c;分别能给研发和IT工程师们减轻多少工作量。 据此&#xff0c;我们搭建了一个工业仿真…

C++/PTA 神坛

C/PTA 神坛 题目要求解题前提及思路凹包算法概念步骤 解题思路 代码tan(y/x)及tan2(y,x)函数 总结 题目要求 在古老的迈瑞城&#xff0c;巍然屹立着 n 块神石。长老们商议&#xff0c;选取 3 块神石围成一个神坛。因为神坛的能量强度与它的面积成反比&#xff0c;因此神坛的面…

记录:自回归 模型在记忆 全随机序列 的潜变量 统计量爆炸现象

只是一个记录 8层12头512维度的 GPT 模型&#xff0c;使用它来记忆 10000 条 512长度 的无序序列&#xff0c;vocab_size 为100。 模型要自回归生成这些序列&#xff0c;不可能依赖局部推理&#xff0c;必须依赖全局视野&#xff0c;即记住前面的序列。 然后统计 最后一个no…

HF宣布在transformers库中引入首个RNN模型:RWKV,一个结合了RNN与Transformer双重优点的模型

RWKV是一个结合了RNN与Transformer双重优点的模型架构。由香港大学物理系毕业的彭博首次提出。简单来说&#xff0c;RWKV是一个RNN架构的模型&#xff0c;但是可以像transformer一样高效训练。今天&#xff0c;HuggingFace官方宣布在transformers库中首次引入RNN这样的模型&…

QGC局域网内连接PX4模拟器JMAVSim

环境 QGroundControl 开源地面站系统; 代码地址: https://github.com/mavlink/qgroundcontrolPX4 开源飞控系统; 代码地址: https://github.com/PX4/PX4-Autopilot QGC可以直接下载运行包. PX4 请根据代码中的说明,进行环境的配置和运行. 通过代码去build地面站和PX4的步骤见官…

【Linux 之五】 Linux中使用fdisk命令实现磁盘分区

最近由于工作的需要&#xff0c;初步研究了uboot中的fastboot实现方式。研究fastboot不可避免的需要了解磁盘分区的相关知识点&#xff0c;在linux下可以使用fdisk命令实现磁盘的分区。好了&#xff0c;下面步入正题。 1. 查看帮助信息&#xff08;fdisk --help&#xff09; …

计算机网络 - 传输层

Transport-Layer Services Transport layer, 传输层主要是完成进程(proces)到进程(process)之间的通讯的. 在传输层之下的IP协议, 提供的是best-effort的传输, 也就是对信息的正确性没有保证, 也就是IP的协议是unreliable的, TCP的协议是在IP协议至少提供可靠的数据传输. UDP…

Word批量更改图片环绕方式与=尺寸大小

前提&#xff1a;一份Word文档里面有100张图片&#xff0c;有大有小&#xff0c;需要将100张图片更改为统一大小&#xff0c;宽度与高度均为5厘米&#xff0c;同时环绕方式也需要改成四周型。 默认Word图片的默认环绕方式为嵌入型&#xff0c;需要统一更改为四周型&#xff0c;…

多元时间序列 | RBF径向基神经网络多变量时间序列预测(Matlab完整程序)

多元时间序列 | RBF径向基神经网络多变量时间序列预测(Matlab完整程序) 目录 多元时间序列 | RBF径向基神经网络多变量时间序列预测(Matlab完整程序)预测结果评价指标基本介绍程序设计参考资料预测结果 评价指标 训练集数据的R2为:0.99805 测试集数据的R2为:0.98351 训练…

二叉树最大宽度_深度优先方式_20230520

二叉树最大宽度_深度优先方式_20230520 前言 给定一颗二叉树&#xff0c;求解其最大宽度。定义每层宽度为该层最左和最右之间的长度&#xff0c;也即左右两个端点之间的所跨越的长度&#xff0c;两个端点直接可能会包含一些延伸到本层的空节点&#xff0c;这些空节点的长度由…

【小沐学NLP】Python实现聊天机器人(OpenAI,模型概述笔记)

&#x1f37a;NLP开发系列相关文章编写如下&#x1f37a;&#xff1a;1&#x1f388;【小沐学NLP】Python实现词云图&#x1f388;2&#x1f388;【小沐学NLP】Python实现图片文字识别&#x1f388;3&#x1f388;【小沐学NLP】Python实现中文、英文分词&#x1f388;4&#x1…

Springcloud1----->Hystrix

目录 雪崩问题服务降级原理实践order降级处理user降级处理 服务熔断原理实践 hystrix&#xff0c;英文意思是豪猪&#xff0c;全是是刺&#xff0c;一种保护机制&#xff0c;即熔断器。 主页&#xff1a;https://github.com/Netflix/Hystrix/ 雪崩问题 在微服务中&#xff0c;…

selenium自动化测试报告_selenium自动化测试断言

一、元素操作方法 方法&#xff1a; 1、.send_keys() # 输入方法 2、.click() # 点击方法 3、.clear() # 清空方法 复制 注意&#xff1a;在输入方法之前一定要清空操作!! # 导包 from time import sleep from selenium import webdriver# 实例化浏览器 driver webdriver…

这才是自动化测试,资深测试构建持续交付体系(高质量)持续集成...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 软件测试-自动化测…

OptimizePI仿真流程

OptimizePI软件介绍 OptimizePI是用于针对PI设计进行优化的一个设计流程&#xff0c;可以针对PDN的设计进行仿真计算&#xff0c;主要功能包括以下几个&#xff1a; PDN前仿真分析PDN后仿真分析PDN阻抗检查去耦电容的回路电感分析芯片的电源管脚的电感分析去耦电容最佳位置评…

一文参透AB斗篷的前世今生

做FP独立站的应该都对cloak斗篷不陌生&#xff0c;cloak已经成为出海营销行业的必备工具之一。尽管使用cloak并不能保证一定有效&#xff0c;但不使用cloak却也是不可行的。今天本文将详细讲解cloak系统的“前世今生”&#xff0c;以便让大家更好地了解cloak系统并能挑选到最适…

【中间件】通过 docker-compose 快速部署 Zookeeper 保姆级教程

文章目录 一、概述二、前期准备1&#xff09;部署 docker2&#xff09;部署 docker-compose 三、创建网络四、Zookeeper 编排部署1&#xff09;下载 Zookeeper2&#xff09;配置3&#xff09;启动脚本 bootstrap.sh4&#xff09;构建镜像 Dockerfile5&#xff09;编排 docker-c…

如何使用ChatGpt来学习和提问【对话ChatGPT】?

ChatGPT的不断发展和进步&#xff0c;我们需要工作中很多时候会用到ChatGPT&#xff0c;那么如何使用ChatGPT来解决我们工作中的问题呢&#xff1f; Q1如何向ChatGPT提问&#xff0c;从而更快解决我们的问题&#xff1f; ChatGPT&#xff1a;以下是向ChatGPT提问的一些提示&a…