当前位置: 首页 > news >正文

【C++】:模板初阶—函数模板|类模板



Blog’s 主页: 白乐天_ξ( ✿>◡❛)

🌈 个人Motto:他强任他强,清风拂山岗!

💫 欢迎来到我的学习笔记!

本文参考博客:一同感受C++模版的所带来的魅力

一、泛型编程思想

  • 首先我们来实现一个swap交换函数。如果学过了C++的函数重载和引用的话,就可以写出swap函数不同参数类型的重载函数。(注意: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;
}

根据上面的代码,我们就可以感觉到,swap交换函数仅仅只是实现一个交换功能,却需要根据参数的类型写出几个很相似的函数,重复相同或者相近的代码。如果又增加了其他类型数据需要进行交换呢?继续重复相同的操作再去实现这个函数吗?

这样操作虽然可行,但是它存在几处缺陷:

  1. 重载的函数仅仅只是类型不同,代码复用吕比较低,只要有新的类型出现,就需要自己再次实现所需类型的相近函数。
  2. 代码的可维护性比较低,一个出错可能所有的重载均出错。

那么既然重载函数的重复率比较高,能否只给出一个标准,然后让他们自己根据这个标准去实现所需要的东西呢?

这个标准就像做月饼用的模具一样:我们只需要放入材料,就可以利用模具做出形状相同的月饼。

后来C++就生成一个类似于模具的东西,将其交给编译器,让编译器根据这个模具自行生成所需代码。这就是泛型编程思想:编写与类型无关的通用代码,是代码复用的一种手段。模板,是泛型编程的基础。

二、函数模板–函数的模板

2.1 概念

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

通过函数模板,可以编写一种通用的函数定义,使其能够是用于多种数据类型,从而提高代码的服用行和灵活性。

2.2 格式

  • 声明一个函数模板需要关键字template<>内部是模板参数,可以使用class或者typename来进行类型的声明(不能用struct)。
  • 然后开始使用模板参数Tn,
template<typename T1,typename T2,typename T3,……,typename Tn>
返回值类型 函数名(参数列表)
{ }
  • 注意:这里的函数模板的参数和普通的函数参数不一样。函数模板参数定义的是<u>类型</u>,而普通函数参数定义的是<u>对象</u>
返回值类型 函数名(参数列表){ }
  • 在了解上面的函数模板后,我们就可以为Swap()函数写一个通用的函数模板了。
//模板类型
template<typename T>
void Swap(T& left, T& right)
{T temp = left;left = right;right = temp;
}
  • 运用:
int main()
{int a1 = 10, a2 = 20;double d1 = 1.1, d2 = 2.2;cout << "a1:" << a1 << " a2:" << a2 << endl;cout << "d1:" << d1 << " d2:" << d2 << endl;Swap(a1, a2);swap(d1, d2);cout << "a1:" << a1 << " a2:" << a2 << endl;cout << "d1:" << d1 << " d2:" << d2 << endl;return 0;
}

运行结果:

调试就可以发现:Swap函数的函数模板可以自己推导传入参数的类型。

  • 那如果给是Swap函数传入不同的参数类型呢?
//模板类型
template<typename T>
void Swap(T& left, T& right)
{T temp = left;left = right;right = temp;
}template<class T1,class	T2>
void func(const T1& x,const T2& y)//不同的类类型
{ }
int main()
{int a1 = 10, a2 = 20;double d1 = 1.1, d2 = 2.2;//传入不同的类型:Swap(a1, d1);	 //error:  message : “void Swap(T &,T &)”: 模板 参数“T”不明确// message : 可能是“double”或    “int”//message: “void Swap(T&, T&)” : 无法从“double”推导出“T & ”的 模板 参数func(a1, d1);	 //rightreturn 0;
}

message : “void Swap(T &,T &)”: 无法从“double”推导出“T &”的 模板参数

  • 那为什么func()函数传入不同的类型的参数却没有问题呢?
    • 原因:传入func()函数的参数并没有用来做编译器无法推导参数类型的操作。Swap()函数是因为编译器无法推导出参数类型。(一个T类型,结果参数是两个类型)
  • 其实在C++中早就已经定义好了swap()这个函数,头文件<utility>,我们可以直接使用。

2.3 原理

2.4 实例化

  • 用不同类型的参数使用函数模板,称为函数模板的实例化。现在我们利用下面这个函数模板来理解实例化概念。
//用函数模板生成对应的函数-->模板的实例化
template<class T>//一个模板参数T
T Add(const T& left, const T& right)
{return left + right;
}

2.4.1 隐式实例化

  1. 概念:
  • 隐式实例化(推导实例化):让编译器根据实参推演模板参数的实际类型,再返回不同类型的数据。
int main()
{int a1 = 10, a2 = 20;double d1 = 10.1, d2 = 20.2;//推导实例化:实参传递给形参,编译器推导出T的类型cout << Add(a1, a2) << endl;cout << Add(d1, d1) << endl;return 0;
}

运行结果:

  1. 缺陷:传入参数类型不同,编译器左右为难、骑虎难下。
int main()
{int a1 = 10, a2 = 20;double d1 = 10.1, d2 = 20.2;cout << Add(a1, d1) << endl;//message : “T Add(const T &,const T &)”: 无法从“double”推导出“const T &”的 模板 参数return 0;
}

d1强制类型转换为int类型,解决类型冲突问题。

cout << Add(a1, (int)d1) << endl;

a1强制类型转换为double类型,解决类型冲突问题。

cout << Add((double)a1, d1) << endl;

③ 如果非要传入不同类型的参数,就应该用不同的模板参数重新定义一个函数模板。

template<class T1& left,class T2& right>
T1 Add(const T1& left,const T2& right)//两个模板参数
{return left + right;
}

2.4.2 显示实例化

  1. 概念
  • 显示实例化:在函数名后面紧跟<>并在其中指定模板参数的实际类型。
//显示实例化:用指定类型来实例化
cout << Add<int>(a1, d1) << endl;
cout << Add<double>(a1, d1) << endl;
  1. 缺陷:其实在某些场景下面我们只能选择显示实例化。
  • 形参部分不是模板参数,而是普通的自定义类型,返回值才是。此时我们无法通过传参来指定这个T的类型,只有外部在调用这个模板时显示指定。
template<class T>
T* Alloc(int n)
{return new T[n];
}
// 有些函数无法自动推,只能显示实例化
int  main()
{// 有些函数无法自动推,只能显示实例化double* p1 = Alloc<double>(10);float* p1 = Alloc<float>(20);int* p2 = Alloc<int>(30);return 0;
}

2.5 模板参数的匹配原则

  1. 一个非模板函数可以和一个同名的函数模板同时存在,而且该函数模板还可以被实例化为这个非模板函数。
//普通函数
int Add(int left, int right)
{return left + right;
}
//函数模板
template<class T>
T Add(T left, T right)
{return left + right;
}
int main()
{Add(1, 2);//调用了普通传参的函数Add<int>(1, 2);//调用了模板函数让其实生成对应的函数return 0;
}
  • 根据调试可以发现:普通函数和函数模板是可以共存的。在进行普通传参时调用的是普通函数;显示指定了类型时,就会调用函数模板生成对应的函数。
  1. 对于非模板函数和同名函数模板,如果其他条件相同,在调用时会优先调用非模板函数而不会从该模板产生一个实例。如果模板可以产生一个更好匹配的函数,那么将会选择模板。
// 普通函数
int Add(int left, int right)
{return left + right;
}// 函数模板
template<class T1, class T2>
T1 Add(T1 left, T2 right)
{return left + right;
}int main()
{Add(1, 2);//调用普通函数,因为参数匹配Add(1, 2.2);///调用函数模板,因为参数不匹配,普通函数不接收此参数。//函数模板可以根据这个类型自动推导return 0;
}
  1. 模板函数不允许自动类型转换,但是普通函数可以进行自动类型转换。
  • 普通函数传入的参数可以发生隐式类型转换,又称【自动类型转换】,下面代码中浮点数自动类型转换为整型数。
// 普通函数,允许自动类型转换
void print(int value) 
{std::cout << "Integer: " << value << std::endl;
}int main() 
{print(3);	print(3.14); // 在这里发生了隐式类型转换,浮点数自动类型转换为整型数return 0;
}
  • 对于函数模板来说是不能进行自动类型转换的。a和b是两个不同的类型,并没有发生自动类型转换,一个模板参数T就使得编译器无法进行自动推导。解决办法就是上面提到的隐式/显示类型转换。第三种方法就是增加模板参数。
template <class T>
void print(T a, T b) {cout << a << " " << b << endl;
}
int main()
{int a = 1;double b = 1.11;print(a, b);//error---message : “void print(T,T)”: 模板 参数“T”不明确return 0;
}

三、类模板–类的模板

3.1 类模板的定义格式

  • 函数模板是加在函数上,类模板是加在类上。
template<class T1, class T2, ..., class Tn>
class 类模板名
{// 类内成员定义
};
  • 类模板都是显示实例化。
template<class T>
class Stack
{
public:Stack(int n = 4)//写构造:_array(new T[n])//不用检查是否失败,失败了直接抛异常,_size(0),_capacity(n){}~Stack(){delete[] _array;_size = _capacity = 0;_array = nullptr;}void Push(const T& x)//传引用传参,避免多余拷贝浪费资源,能用引用就尽量使用引用{//空间不够用:扩容if (_size == _capacity){//不能使用realloc,无构造//C++无自动扩容的概念,扩容需要手动T* temp = new T[_capacity * 2];//扩容两倍,size不变memcpy(temp, _array, sizeof(T) * _size);//拷贝数据delete[] _array;//delete一次即可:释放旧空间_array = temp;//指向新空间_capacity *= 2;}_array[_size++] = x;}
//其他功能…………
private:T* _array;size_t _capacity;size_t _size;
};
int main()
{//类模板都是显示实例化Stack<int> st1;//     intst1.Push(1);st1.Push(2);st1.Push(3);Stack<double> st2;//  doublest2.Push(1.1);st2.Push(2.2);st2.Push(3.3);return 0;
}
  • 我们在C语言中学习栈的时候就有一个typedef的操作,功能类似于这里的模板。既然如此,那类模板的意义是什么呢?
    • C语言中的栈只能实现某一种类型,而C++可以一个栈存入int类型,另一个栈存入double类型。
    • 就像在这里一样,实现的栈的结构基本都是一样的,不一样的只是存入栈的数据类型是不一样的。
  • 如果在这里进行声明和定义的分离?
    • 定义的函数模板只能给当前的函数或者当前的类使用,每个函数模板都需要定义自己的模板参数,按需定义。当进行声明和定义分离时,需要在函数前面单独声明类模板,并指定类域。下面就是类模板中的成员函数在类外面实现所需要变化成的模板函数。
template<class T>
class Stack
{
public:Stack(int n = 4):_array(new T[n]), _size(0), _capacity(n){}~Stack(){delete[] _array;_size = _capacity = 0;_array = nullptr;}void Push(const T& x);//其他功能……
private:T* _array;size_t _capacity;size_t _size;
};
//指定类域不行,还需要声明一下类模板
template<class T>
void Stack<T>::Push(const T& x)//传引用传参,避免多余拷贝浪费资源,能用引用就尽量使用引用
{//空间不够用:扩容if (_size == _capacity){T* temp = new T[_capacity * 2];memcpy(temp, _array, sizeof(T) * _size);delete[] _array;_array = temp;_capacity *= 2;}_array[_size++] = x;
}
  • 如果我们将上面分离后新声明的T改为X,能否编译?
    • 能的。T只是一个符号而已,我们实际上在调用这个函数时,并没有T或者X的概念。
  • 注意:模板不支持声明和定义分离到两个不同文件中去。(即使可以,也相当繁琐。)

3.2 类模板的实例化

  • 类模板实例化和函数模板实例化不同。类模板实例化需要在类模板名称后面跟<>。然后将实例化的类型放在<>中即可,类模板名字不是真正的类,而实例化的结果才是真正的类。
int main()
{//类模板都是显示实例化Stack<int> st1;//     intst1.Push(1);st1.Push(2);st1.Push(3);Stack<double> st2;//  doublest2.Push(1.1);st2.Push(2.2);st2.Push(3.3);return 0;
}

以上就是本文所要介绍的所有类容,感谢您的阅读!🌹

在这里插入图片描述

相关文章:

【C++】:模板初阶—函数模板|类模板

✨ Blog’s 主页: 白乐天_ξ( ✿&#xff1e;◡❛) &#x1f308; 个人Motto&#xff1a;他强任他强&#xff0c;清风拂山岗&#xff01; &#x1f4ab; 欢迎来到我的学习笔记&#xff01; 本文参考博客&#xff1a;一同感受C模版的所带来的魅力 一、泛型编程思想 首先…...

Java 远程执行服务器上的命令

在Java中使用JSch库执行远程服务器上的命令是一种常见的做法&#xff0c;特别是在需要自动化运维任务或者进行远程文件操作时。以下是基于Codekru网站提供的示例&#xff0c;展示如何使用JSch库在远程服务器上执行单个或多个命令。 准备工作 首先&#xff0c;确保您的项目中已…...

3DMax基础- 创建基础模型

目录 零.软件简介 一. 标准基本型 长方体 圆锥体 球体 圆柱体 管状体 圆环 四棱锥 茶壶 平面​编辑 加强型文本 二. 扩展基本体 三.复合对象 变形 散布 一致 连接 图形合并 布尔 并集 合并 交集 差集 四.门和窗 门 窗 植物,栏杆,墙 零.软件简介 3…...

JavaScript 知识点(从基础到进阶)

&#x1f30f;个人博客主页&#xff1a;心.c ​ 前言&#xff1a;JavaScript已经学完了&#xff0c;和大家分享一下我的笔记&#xff0c;希望大家可以有所收获&#xff0c;花不多说&#xff0c;开干&#xff01;&#xff01;&#xff01; &#x1f525;&#x1f525;&#x1f5…...

计算机网络知识点复习——TCP协议的三次握手与四次挥手(连接与释放)

TCP协议的三次握手与四次挥手&#xff08;连接与释放&#xff09; 一、前言二、简单的知识准备1. TCP协议的主要特点2. TCP报文段 三、TCP连接的建立&#xff08;三次握手&#xff09;四、TCP连接的释放&#xff08;四次挥手&#xff09;五、TCP连接与释放的总结六、结束语 一、…...

SpringDataJPA系列(7)Jackson注解在实体中应用

SpringDataJPA系列(7)Jackson注解在实体中应用 常用的Jackson注解 Springboot中默认集成的是Jackson&#xff0c;我们可以在jackson依赖包下看到Jackson有多个注解 一般常用的有下面这些&#xff1a; 一个实体的示例 测试方法如下&#xff1a; 按照上述图片中的序号做个简…...

【Spring Boot 3】【Web】统一封装 HTTP 响应体

【Spring Boot 3】【Web】统一封装 HTTP 响应体 背景介绍开发环境开发步骤及源码工程目录结构总结背景 软件开发是一门实践性科学,对大多数人来说,学习一种新技术不是一开始就去深究其原理,而是先从做出一个可工作的DEMO入手。但在我个人学习和工作经历中,每次学习新技术总…...

Linux如何做ssh反向代理

SSH反向代理是一种通过SSH协议实现的安全远程访问方式&#xff0c;它允许客户端通过SSH连接到一台具有公网IP的代理服务器&#xff0c;然后这台代理服务器再将请求转发给内部网络中的目标主机。以下是实现SSH反向代理的步骤&#xff1a; 一、准备工作 确保服务器配置&#xff…...

Verilog语法+:和-:有什么用?

Verilog语法:和-:主要用于位选择&#xff0c;可以让代码更简洁。 一、位选择基础 在Verilog中&#xff0c;位选择可以通过直接索引来实现&#xff0c;例如&#xff1a; reg [7:0] data; wire select_a; wire [2:0] select_b; assign select_a data[3]; assign select_b …...

stm32F103 串口2 中断 无法接收指定字符串 [已解决]

stm32F103 串口2中断接收指定字符串 USART 初始化和中断配置示例中断处理函数示例关键点总结 确保在串口配置中正确使能空闲中断 ( USART_IT_IDLE) 是关键。这个中断可以帮助你在串口接收一帧数据完成后&#xff0c;进行相应的处理和分析。 为了确保你在串口配置时能避免类似问…...

Matlab/Simulink和AMEsim联合仿真(以PSO-PID算法为例)

目录 安装软件和配置环境变量 Matlab/Simulink和AMEsim联合仿真详细流程 非常重要的一点 Simulink模型和AMEsim模型用S-Function建立连接 从AMEsim软件打开Matlab Matlab里的设置 Matlab的.m文件修改&#xff08;对于PSO-PID算法&#xff09; 运行程序 我印象中好像做过…...

超声波测距模块HC-SR04(基于STM32F103C8T6HAL库)

超声波测距模块参考资料 1.电路连接及引脚配置 触发信号PA3只需要输出10us的高电平&#xff0c;所以直接设置成 普通的GPIO端口即可&#xff1b;回响信号使用外部中断&#xff0c;上升沿信号产生外部中断&#xff0c;打开定时器&#xff0c;下降沿再产生一次中断&#xff0c;读…...

Go语言结构体和元组全面解析

Go语言中的复合类型与其应用 在编程中&#xff0c;标准类型虽然方便&#xff0c;但无法满足所有需求。Go通过支持结构体和元组类型&#xff0c;为开发者提供了自定义数据类型的能力。本文将介绍如何定义结构体、如何使用指针操作结构体、如何通过元组返回多个值等内容&#xf…...

集成电路学习:什么是SDK软件开发工具包

SDK&#xff1a;软件开发工具包 SDK&#xff0c;即Software Development Kit&#xff08;软件开发工具包&#xff09;&#xff0c;是一套由软件提供商或其他组织提供的开发工具集合。这些工具旨在帮助开发者更快速、更便捷地创建、测试和部署软件应用程序。以下是对SDK的详细解…...

java后端如何发送http请求

用java后端发送请求需要用到的一个工具包为HttpClient。HttpClient是Apache的一个子项目&#xff0c;是高效的、功能丰富的支持HTTP协议的客户端编程工具包。 引入依赖 <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId&…...

装WebVideoCreator记录

背景&#xff0c;需要在docker容器内配置WebVideoCreator环境&#xff0c;配置npm、node.js WebVideoCreator地址&#xff1a;https://github.com/Vinlic/WebVideoCreator 配置环境&#xff0c;使用这个教程&#xff1a; linux下安装node和npm_linux离线安装npm-CSDN博客 1…...

【编程底层思考】什么是GC Roots

在Java虚拟机&#xff08;JVM&#xff09;中&#xff0c;GC Roots是垃圾收集&#xff08;Garbage Collection&#xff0c;GC&#xff09;过程中的起点&#xff0c;用于确定对象是否可被回收。GC Roots集合是一组必须活跃的&#xff08;即必须保留在内存中的&#xff09;引用&am…...

[STL --stack_queue详解]stack、queue,deque,priority_queue,容器适配器

stack stack介绍 1、stack是一种容器适配器&#xff0c;专门用在具有后进先出操作的上下文环境中&#xff0c;其删除只能从容器的一端进行元素的插入与提取操作。 2、stack是作为容器适配器被实现的&#xff0c;容器适配器即是对特定类封装作为其底层的容器&#xff0c;并提供…...

240907-Gradio插入Mermaid流程图并自适应浏览器高度

A. 最终效果 B. 示例代码 import gradio as grmermaid_code """ <iframe srcdoc <!DOCTYPE html> <html><head><meta charset"utf-8" /><meta name"viewport" content"widthdevice-width" />…...

ubuntu 安装python3 教程

本篇教程,主要介绍如何在Ubuntu上安装python3教程。 1、查看是否有python 在安装前,首先看看自己系统上,是否存在python环境,可能有些系统,默认就安装过python,如果已经有python了,可以直接跳过安装教程。 2、安装步骤 apt update && apt install -y python3 p…...

MongoDB学习和应用(高效的非关系型数据库)

一丶 MongoDB简介 对于社交类软件的功能&#xff0c;我们需要对它的功能特点进行分析&#xff1a; 数据量会随着用户数增大而增大读多写少价值较低非好友看不到其动态信息地理位置的查询… 针对以上特点进行分析各大存储工具&#xff1a; mysql&#xff1a;关系型数据库&am…...

python如何将word的doc另存为docx

将 DOCX 文件另存为 DOCX 格式&#xff08;Python 实现&#xff09; 在 Python 中&#xff0c;你可以使用 python-docx 库来操作 Word 文档。不过需要注意的是&#xff0c;.doc 是旧的 Word 格式&#xff0c;而 .docx 是新的基于 XML 的格式。python-docx 只能处理 .docx 格式…...

多种风格导航菜单 HTML 实现(附源码)

下面我将为您展示 6 种不同风格的导航菜单实现&#xff0c;每种都包含完整 HTML、CSS 和 JavaScript 代码。 1. 简约水平导航栏 <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport&qu…...

如何在最短时间内提升打ctf(web)的水平?

刚刚刷完2遍 bugku 的 web 题&#xff0c;前来答题。 每个人对刷题理解是不同&#xff0c;有的人是看了writeup就等于刷了&#xff0c;有的人是收藏了writeup就等于刷了&#xff0c;有的人是跟着writeup做了一遍就等于刷了&#xff0c;还有的人是独立思考做了一遍就等于刷了。…...

【Android】Android 开发 ADB 常用指令

查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...

Golang——7、包与接口详解

包与接口详解 1、Golang包详解1.1、Golang中包的定义和介绍1.2、Golang包管理工具go mod1.3、Golang中自定义包1.4、Golang中使用第三包1.5、init函数 2、接口详解2.1、接口的定义2.2、空接口2.3、类型断言2.4、结构体值接收者和指针接收者实现接口的区别2.5、一个结构体实现多…...

DBLP数据库是什么?

DBLP&#xff08;Digital Bibliography & Library Project&#xff09;Computer Science Bibliography是全球著名的计算机科学出版物的开放书目数据库。DBLP所收录的期刊和会议论文质量较高&#xff0c;数据库文献更新速度很快&#xff0c;很好地反映了国际计算机科学学术研…...

MFE(微前端) Module Federation:Webpack.config.js文件中每个属性的含义解释

以Module Federation 插件详为例&#xff0c;Webpack.config.js它可能的配置和含义如下&#xff1a; 前言 Module Federation 的Webpack.config.js核心配置包括&#xff1a; name filename&#xff08;定义应用标识&#xff09; remotes&#xff08;引用远程模块&#xff0…...

macOS 终端智能代理检测

&#x1f9e0; 终端智能代理检测&#xff1a;自动判断是否需要设置代理访问 GitHub 在开发中&#xff0c;使用 GitHub 是非常常见的需求。但有时候我们会发现某些命令失败、插件无法更新&#xff0c;例如&#xff1a; fatal: unable to access https://github.com/ohmyzsh/oh…...

一些实用的chrome扩展0x01

简介 浏览器扩展程序有助于自动化任务、查找隐藏的漏洞、隐藏自身痕迹。以下列出了一些必备扩展程序&#xff0c;无论是测试应用程序、搜寻漏洞还是收集情报&#xff0c;它们都能提升工作流程。 FoxyProxy 代理管理工具&#xff0c;此扩展简化了使用代理&#xff08;如 Burp…...