【 C++ 】模板初阶

news/2024/9/19 18:48:05/ 标签: c++, 数据结构, 开发语言

目录

​编辑

一、泛型编程

二、函数模板

1.概念

2.格式

3.原理

4.匹配原则

三、类模板

1.定义格式

2.类模板的实例化


一、泛型编程

泛型编程是一种编程范式,它允许在编写代码时使用一种通用的数据类型或算法,以便在不同的数据类型上进行操作,而不需要为每种数据类型编写专门的代码。泛型编程的目标是提高代码的重用性和可扩展性。

泛型编程:编写与类型无关的通用代码,是代码复用的一种手段。模板是泛型编程的基础。

当我们想对不同的类型实现交换函数,我们以前的做法是写多个重载函数:

int Swap(int& a, int& b)
{int temp = a;a = b;b = temp;
}void Swap(double& a, double& b)
{double temp = a;a = b;b = temp;
}

使用函数重载虽然可以实现,但是有一下几个不好的地方:

  1. 重载的函数仅仅是类型不同,代码复用率比较低,只要有新类型出现时,就需要用户自己增加对应的函数

  2. 代码的可维护性比较低,一个出错可能所有的重载均出错

当我写代码时就想着,要是编译器能够自动识别出类型套用到函数里去就好了。

但是现在,实现了!!!

有请出我们的模板


二、函数模板

1.概念

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

2.格式

template<typename T1, typename T2,......,typename Tn>
返回值类型 函数名(参数列表){}
注:这里的typename 是用来定义模板参数关键字,也可以使用class(切记:不能使用struct代替
class)
现在我们有了模板,如何将swap函数变成模板呢:
template<typename T>
void Swap(T a, T b)
{T temp = a;a = b;b = temp;
}

3.原理

模板本身不能调用,模板是生成具体的函数

函数模板是一个蓝图,它本身并不是函数,是编译器用使用方式产生特定具体类型函数的模具。
所以其实模板就是将本来应该我们做的重复的事情交给了编译器

在编译器编译的时候,编译器会根据实参传给形参来推断对应的类型,再用对应的类型来生成函数。

template<typename T>
void Swap(T a, T b)
{T temp = a;a = b;b = temp;
}int main()
{int a = 1, b = 2;double da = 1.3, db = 1.6;Swap(a, b);Swap(da, db);return 0;
}

所以在这里,a、b和da、db它们所调用的函数不是同一个函数。而是编译器根据推导的类型会生成相应的类型函数。

那如果我们将俩个不同类型的传入Swap模板呢?

可以看到编译器报错了。

但如果我们就想传不同类型应该怎么办呢?

用函数模版生成对应的函数 -> 模版的实例化

方法1:推导实例化

template<typename T>
void Swap(T a, T b)
{T temp = a;a = b;b = temp;
}int main()
{int a = 1, b = 2;double da = 1.3, db = 1.6;Swap(a, b);Swap(da, db);//1.推导实例化Swap(a, (int)da);Swap((double)b, db);return 0;
}

方法2:显示实例化

template<typename T>
void Swap(T a, T b)
{T temp = a;a = b;b = temp;
}int main()
{int a = 1, b = 2;double da = 1.3, db = 1.6;//隐式实例化:让编译器根据实参推演模板参数的实际类型Swap(a, b);Swap(da, db);//1.推导实例化Swap(a, (int)da);Swap((double)b, db);//2.显示实例化:在函数名后的<>中指定模板参数的实际类型Swap<int>(a, da);return 0;
}

在显示实例化中,如果类型不匹配,编译器会进行隐式类型转换。如果无法转换,则编译器会进行报错。在上面的例子中,编译器就对da进行了隐式类型转换,转换成int类型的数据。

方法3:

当你就是不想进行任何的转换,那么这时你还可以对模板进行修改,修改成多参数模板:

template<typename T1,typename T2>
void Swap(T1 a, T2 b)
{T1 temp = a;a = b;b = temp;
}int main()
{int a = 1;double da = 1.1;Swap(1, 1.1);return 0;
}

注:当类型T没有在函数的形参中出现时,必须进行显示实例化:

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

这是因为,编译器无法通过实参传给的形参来判断类型,所以必须明确指出类型给编译器。


4.匹配原则

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

个非模板函数

2. 对于非模板函数和同名函数模板,如果其他条件都相同,在调动时会优先调用非模板函数而

不会从该模板产生出一个实例。如果模板可以产生一个具有更好匹配的函数, 那么将选择模

3. 模板函数不允许自动类型转换,但普通函数可以进行自动类型转换

template<class T>
void Swap(T a, T b)//1
{T temp = a;a = b;b = temp;
}void Swap(int a, int b)//2
{int temp = a;a = b;b = temp;
}template<class T1,class T2>
void Swap(T1 a, T2 b)//3
{T1 temp = a;a = b;b = temp;
}int main()
{int a = 1, b = 2;double da = 1.1, db = 2.2;Swap(a, b);//与非模板函数匹配,走第二个Swap<int>(a, b);//指明要用模板,走第一个Swap(da, db);//比起非模板函数,T1、T2模板更加匹配,走第三个return 0;}

三、类模板

1.定义格式

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

用栈举一个类模板的例子:

template<typename T>
class Stack
{
public:Stack(int n=4):_arr(new T[n]),_capacity(0),_size(0){}void Push(const T& data){if (_size == _capacity)//当栈已经满了的时候{T temp = new T[_capacity * 2];memcpy(temp, _arr, sizeof(T) * _size);//这一步类似于以前的reallocdelete[]_arr;_arr = temp;_capacity *= 2;}_arr[_size++] = data;}
private:T* _arr;int _capacity;int _size;
};int main()
{Stack<int>s1;s1.Push(1);s1.Push(2);Stack<double>s2;s2.Push(1.1);s2.Push(2.2);return 0;
}

一个模板对应一个类或者函数,所以当我们想将类里面的函数放在类外面来进行使用,那必须重新为这个函数写一个模板,如下:

template<typename T>
class Stack
{
public:Stack(int n = 4):_arr(new T[n]), _capacity(0), _size(0){}void Push(const T& data);
private:T* _arr;int _capacity;int _size;
};template<class T>
void Stack<T>::Push(const T& data)
{if (_size == _capacity)//当栈已经满了的时候{T temp = new T[_capacity * 2];memcpy(temp, _arr, sizeof(T) * _size);//这一步类似于以前的reallocdelete[]_arr;_arr = temp;_capacity *= 2;}_arr[_size++] = data;
}int main()
{Stack<int>s1;s1.Push(1);s1.Push(2);Stack<double>s2;s2.Push(1.1);s2.Push(2.2);return 0;
}

注意:模板不建议将声明和定义分离到两个文件


2.类模板的实例化

类模板实例化与函数模板实例化不同,类模板实例化需要在类模板名字后跟<>,然后将实例化的

类型放在<>中即可,类模板名字不是真正的类,而实例化的结果才是真正的类。

// Stack是类名,Stack<int>才是类型
Stack<int> st1; // int
Stack<double> st2; // double



如果这篇文章有帮助到你,请留下您珍贵的点赞、收藏+评论,这对于我将是莫大的鼓励!学海无涯,共勉!😘😊😗💕💕😗😊😘





http://www.ppmy.cn/news/1523971.html

相关文章

vue2、vue3生成二维码

Vue2版&#xff1a; 工具&#xff1a;使用 qrcodejs插件来生成二维码 安装&#xff1a;npm install qrcodejs2 qrcodejs官网地址&#xff1a; https://davidshimjs.github.io/qrcodejs/https://davidshimjs.github.io/qrcodejs/ 代码示例&#xff1a; <template><…

秒懂:进程上下文切换

1、进程上下文的组成部分 进程的物理实体&#xff08;代码和数据等&#xff09;和支持进程运行的环境合称为进程的上下文&#xff1a; 由进程的程序块、数据块、运行时的堆和用户栈&#xff08;两者通称为用户堆栈&#xff09;等组成的用户空间信息被称为用户级上下文。 由进程…

「数据科学」Seaborn图形可视化,概要介绍及实践

​Seaborn是一个使用Python语言编程&#xff0c;绘制各种统计图形的Python第三方库。Seaborn可以使用Python语言中的Pandas处理数据&#xff0c;基于matplotlib实现的&#xff0c;高级绘图环境。 Seaborn库简介 Seaborn能够帮助你&#xff0c;更好的探索和理解你的数据。其绘…

ios免签H5

1、windows下载mobileconfig文件制作工具&#xff0c;可在csdn搜索iPhone_Mobileconfig_Tool下载安装&#xff1b;IOS 从APP Store 下载Apple Configurator 2 2、用申请的域名SSL证书给mobieconfig文件签名&#xff0c;最好下载Apache证书&#xff0c;里面包含 AE86211.crt…

golang学习笔记11——Go 语言的并发与同步实现详解

推荐学习文档 golang应用级os框架&#xff0c;欢迎star基于golang开发的一款超有个性的旅游计划app经历golang实战大纲golang优秀开发常用开源库汇总golang学习笔记01——基本数据类型golang学习笔记02——gin框架及基本原理golang学习笔记03——gin框架的核心数据结构golang学…

开发中的网络问题逻辑推理分析

基于TCP/IP的逻辑推理&#xff0c;大部分软件从业人员都不是很懂&#xff0c;导致很多问题都被误认为诡异问题。有些人是惧怕TCP/IP网络书籍中的复杂知识内容&#xff0c;有的是被wireshark[1]显示的深红色内容所干扰。 经典案例1&#xff1a; 例如有一个DBA遇到了性能问题&a…

【数据结构】2——二叉树遍历

数据结构2——二叉树遍历 单纯记录 文章目录 数据结构2——二叉树遍历一、二叉树遍历1&#xff0c;先序遍历&#xff1a;根左右递归实现非递归实现&#xff08;栈&#xff09; 2.中序遍历&#xff1a;左根右递归非递归 3 .后序遍历&#xff1a;左右根递归非递归&#xff08;两…

GDB 查看汇编

查看汇编 x disassemble

Linux 中 Tail 命令的 9 个实用示例

引言 我们作为 Linux 用户&#xff0c;经常会操作那些在后台长时间运行的进程&#xff0c;这些进程被称作守护进程或服务。例如 Secure Shell (sshd)、Network Manager (networkd)、Volume Manager (LVM)、Cron 等都是服务的典型例子&#xff0c;这样的服务还有很多。 在许多情…

Windows安装HeidiSQL教程(图文)

一、软件简介 HeidiSQL是一款开源的数据库管理工具&#xff0c;主要用于管理MySQL、MariaDB、SQL Server、PostgreSQL和SQLite等数据库系统。它提供了直观的用户界面&#xff0c;使用户可以轻松地连接到数据库服务器、执行SQL查询、浏览和编辑数据、管理数据库结构等操作。 跨…

Java 内部类包含静态内部类(Static Nested Class)、非静态内部类(Inner Class)

在 Java 中&#xff0c;内部类主要分为以下几种类型&#xff1a; 成员内部类&#xff08;Member Inner Class&#xff09;静态内部类&#xff08;Static Nested Class&#xff09;局部内部类&#xff08;Local Inner Class&#xff09;匿名内部类&#xff08;Anonymous Inner …

芋道系统excel导出,springcloud+vue3

前端代码&#xff1a; <el-form-item><el-buttontype"success"plainclick"handleExport":loading"exportLoading"v-hasPermi"[teach:course-manage:export]"><Icon icon"ep:download" class"mr-5px&quo…

【mysql】mysql之主从延迟复制测试场景

本站以分享各种运维经验和运维所需要的技能为主 《python零基础入门》&#xff1a;python零基础入门学习 《python运维脚本》&#xff1a; python运维脚本实践 《shell》&#xff1a;shell学习 《terraform》持续更新中&#xff1a;terraform_Aws学习零基础入门到最佳实战 《k8…

当水泵遇上物联网:智能水务新时代的浪漫交响

在当代科技的宏伟乐章中&#xff0c;物联网&#xff08;IoT&#xff09;技术宛如一位技艺高超的指挥家&#xff0c;引领着各行各业迈向智能化的新纪元。当这股创新浪潮涌向古老的水务行业时&#xff0c;一场前所未有的“智能水务”革命便悄然上演&#xff0c;而水泵——这一传统…

小宝宝的好伙伴Baby Buddy

好友 Eduna 前天半夜告诉我&#xff0c;Docker 的下载已经恢复&#xff0c;又可以愉快的玩耍了&#xff0c;大家赶紧去试试吧~ 什么是 Baby Buddy &#xff1f; Baby Buddy 是宝宝的好伙伴&#xff01;能帮助宝爸、宝妈、及护理人员跟踪宝宝的睡眠、喂食、换尿布、趴着的时间等…

【Linux】循序进阶学运维-服务篇-tomcat入门

[rootgaosh-1 jdk]# ##### []( )2\. 配置java环境\[rootgaosh-1 jdk\]# vim /etc/profile![在这里插入图片描述](https://img-blog.csdnimg.cn/20200801062552258.png)export JAVA_HOME/root/jdk/jdk1.8.0_131 ## 路径一定要对 export PATH J A V A H O M E / b i n : JAVA_HO…

Collections

Collections 是 Java 中的一个实用工具类&#xff0c;提供了一系列静态方法来操作集合。以下是其详细介绍&#xff1a; 前置知识 在 Java 中&#xff0c;可变参数&#xff08;Varargs&#xff09;允许方法接受可变数量的参数。使用可变参数时&#xff0c;可以传递任意数量的参…

EmguCV学习笔记 C# 11.3 DNN其它

版权声明&#xff1a;本文为博主原创文章&#xff0c;转载请在显著位置标明本文出处以及作者网名&#xff0c;未经作者允许不得用于商业目的。 EmguCV是一个基于OpenCV的开源免费的跨平台计算机视觉库,它向C#和VB.NET开发者提供了OpenCV库的大部分功能。 教程VB.net版本请访问…

Python Web 框架篇:Flask、Django、FastAPI介绍及其核心技术

Python Web 框架篇&#xff1a;Flask、Django、FastAPI介绍及其核心技术 目录 &#x1f40d; Flask Flask 核心概念&#xff08;路由、视图函数、模板渲染&#xff09;Flask Blueprint 模块化应用Flask 扩展&#xff08;Flask-SQLAlchemy、Flask-WTF、Flask-Migrate 等&#…

Centos7.9部署Gitlab-ce-16.9

一、环境信息 软件/系统名称版本下载地址备注Centos77.9.2009https://mirrors.nju.edu.cn/centos/7.9.2009/isos/x86_64/CentOS-7-x86_64-DVD-2009.isogitlab-cegitlab-ce-16.9.1https://mirror.tuna.tsinghua.edu.cn/gitlab-ce/yum/el7/gitlab-ce-16.9.1-ce.0.el7.x86_64.rpm…