C++初阶:初识模板

        在之前的C与C++编程中,针对实现同样类型功能的函数我们学会使用了函数重载,终于可以不用记忆多个功能相同但是函数名不同的函数了喵。但是在实现的时候仍然显得有点不太方便,有些冗余。世界是懒人的世界,为了方便懒人的使用,模板应运而生~

目录

一、引子

二、函数模板

1.概念

2.格式

3.原理

4.函数模板实例化

(1)隐式实例化

(2)显示实例化

5.匹配规则

三、类模板

1.类模板的定义格式

2.类模板的实例化


一、引子

        实现能够对相同类型进行交换的函数:

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;
}

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

  • 重载的函数仅仅是类型不同,代码复用率比较低,只要有新类型出现时,就需要用户自己增加对应的函数;
  • 代码的可维护性比较低,一个出错可能所有的重载均出错。

        在前面的学习中,我们已经学习了类的有关知识,由我们定义一个类之后,我们可以以这个类为模版来创建对象,那么函数可否这样呢?答案是可以的,这要用到一个关键字template。针对上面的代码,可以用下面的模板函数来替代。

template <typename T>
void Swap(T &left, T &right)
{T temp = left;left = right;right = temp;
}

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

二、函数模板

1.概念

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

2.格式

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

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

 例子见上面的swap解决方案。

注意:typename是用来定义模板参数关键字,也可以使用class(切记:不能使用struct代替class)

3.原理

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

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

4.函数模板实例化

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

(1)隐式实例化

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

(2)显示实例化

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
template<class T>
T Add(const T& left, const T& right)
{return left + right;
}
int main(void)
{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版本
}

(2)对于非模板函数和同名函数模板,如果其他条件都相同,在调动时会优先调用非模板函数而不会从该模板产生出一个实例。如果模板可以产生一个具有更好匹配的函数, 那么将选择模板。(毕竟是懒人创造的东西,人为写好了,编译器也可以选择偷点小懒)

// 专门处理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<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;
}

2.类模板的实例化

        类模板实例化与函数模板实例化不同,类模板实例化需要在类模板名字后跟<>,然后将实例化的类型放在<> 中即可,类模板名字不是真正的类,而实例化的结果才是真正的类

 // Vector类名,Vector<int>才是类型Vector<int> s1;Vector<double> s2;

        在日后学习数据结构与算法时,模板会被经常使用,届时再会详细讲述。

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

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

相关文章

YOLOv8 服务器与本地tensorboard映射

TensorBoard: Start with ‘tensorboard --logdir runs/detect/train12’, view at http://localhost:6006/ 服务器端输入后本地一直打不开&#xff0c;无法访问。 解决方法&#xff1a;对服务器6006端口与本地端口进行映射&#xff0c;从而完成本地查看TensorBoard 本地cmd输…

多个OncePerRequestFilter过滤器实现的使用及顺序

多个OncePerRequestFilter过滤器实现的使用及顺序 作用 在一次外部请求中只过滤一次, 对于服务器内部之间的forward等请求&#xff0c;不会再次执行过滤方法可以定义多个实现类, 各自处理各自的过滤工作, 并指定过滤顺序 使用场景 token校验有效性Xss处理敏感词汇过滤 … …

驶向高效巅峰:Nginx高并发性能优化实战指南与场景案例拆解

身处瞬息万变的互联网世界&#xff0c;高并发场景下服务器的性能优化尤为重要&#xff0c;而Nginx正是这一领域的超级舵手。本文将深入浅出地揭示Nginx在高并发环境下的性能优化之道&#xff0c;并通过具体场景配置案例&#xff0c;助你掌握这一关键技术&#xff0c;提升服务器…

静态网络配置

一、查看网络命令 1.命令行查看网络配置 1、查看ip\硬件设备-网卡 ifconfig -a ifconfig ens160 网卡名称 ip addr show ip addr show ens160 nmcli device show ens160 nmcli con up ens160 2、主机名称 hostname hostname hfj.huaxia.com 3、查看路由和网关 rou…

慎投!共10本「On Hold」期刊被剔除,2本中科院TOP仍在调查中!

2024年3月18日&#xff0c;科睿唯安本年度第三次更新了Web of Science核心期刊目录。 此次SCIE/SSCI期刊目录共8本期刊发生变动&#xff0c;情况如下&#xff1a; 经小编查询&#xff0c;此次更新后&#xff0c;共有7本“On Hold”期刊被数据库剔除&#xff0c;其中还包括2024年…

Winsock编程入门和枚举协议

Winsock的初始化和清除代码类似如下; if ((ret = WSAStartup(MAKEWORD(2,2), &wsadata)) != 0) { wsprintf(buf,TEXT("winsock初始化失败,错误:%d"), ret); ...... return 0; } 。。。。。。 if (WSACleanup() == SOCKET_ERRO…

手撕算法-最长公共子序列(二)

最长公共子序列(二) 分析&#xff1a;典型的动态规划&#xff0c;直接看代码了。 代码&#xff1a; import java.util.*;public class Solution {/*** 代码中的类名、方法名、参数名已经指定&#xff0c;请勿修改&#xff0c;直接返回方法规定的值即可** longest common sub…

VS+QT Debug正常但Release无法识别头文件

&#xff01;&#xff01;&#xff01;&#xff0c;这个问题一般是在第一次编译的时候遇见的&#xff0c;包括之前使用debug也是 在Qt Installation一定要修改成自己版本的编译器&#xff0c;修改一次以后基本是不用再修改的

【机器学习】TinyML的介绍以及在运动健康领域的应用

&#x1f9d1; 作者简介&#xff1a;阿里巴巴嵌入式技术专家&#xff0c;深耕嵌入式人工智能领域&#xff0c;具备多年的嵌入式硬件产品研发管理经验。 &#x1f4d2; 博客介绍&#xff1a;分享嵌入式开发领域的相关知识、经验、思考和感悟,欢迎关注。提供嵌入式方向的学习指导…

将图像根据注释文件划分

import os import json import shutil# 完整图像文件夹路径 img_dir data\sodaa\datasets# 注释文件夹路径 train_ann_dir data\sodaa\Annotations\\train val_ann_dir data\sodaa\Annotations\\val test_ann_dir data\sodaa\Annotations\\test# 输出文件夹路径 output_dir…

5465: 【搜索】奶牛干饭

题目描述 农场主John把农场分为了一个 r 行 c 列的矩阵&#xff0c;并发现奶牛们无法通过其中一些区域。此刻&#xff0c;Bessie 位于坐标为 (1,1) 的区域&#xff0c;并想到坐标为 (r,c) 的牛棚享用晚餐。她知道&#xff0c;以她所在的区域为起点&#xff0c;每次移动至相邻的…

全球盲盒火热下,海外盲盒APP助力我国盲盒出海

盲盒具有不确定性&#xff0c;与各类热门影视动漫合作推出的专属盲盒商品&#xff0c;吸引了无数年轻人&#xff0c;成为了年轻人的娱乐消费首选方式。 在互联网电商的推动下&#xff0c;盲盒在全球内的市场规模迅速扩大。受到市场增长的影响&#xff0c;各类资本公司也纷纷进…

【Python多线程】的进阶讲解

Python多线程 1. 前言2. threading 模块的基本用法3. Thread类4. 锁&#xff08;Locks&#xff09;5. 守护线程&#xff08;Daemon Threads&#xff09;6. 运用场景7. 弊端 1. 前言 Python中的多线程通过threading模块来实现&#xff0c;它允许你并发执行多个线程&#xff0c;…

深入浅出前端本地储存(1)

引言 2021 年&#xff0c;如果你的前端应用&#xff0c;需要在浏览器上保存数据&#xff0c;有三个主流方案&#xff1a; CookieWeb Storage (LocalStorage)IndexedDB 这些方案就是如今应用最广、浏览器兼容性最高的三种前端储存方案 今天这篇文章就聊一聊这三种方案的历史…

基于python的4s店客户管理系统

技术&#xff1a;pythonmysqlvue 一、背景 进入21世纪网络和计算机得到了飞速发展&#xff0c;并和生活进行了紧密的结合。目前&#xff0c;网络的运行速度以达到了千兆&#xff0c;覆盖范围更是深入到生活中的角角落落。这就促使管理系统的发展。网上办公可以实现远程处理事务…

pyvista可视化代码优化

同时显示多组点云 import os import glob import randomimport pyvista as pvdef display_multi_mesh(meshes: list, titlesNone, point_size3, opacity0.9):num len(meshes)pl pv.Plotter(shape(1, num))pl.set_background([0.9, 0.9, 0.9])for i in range(num):pl.subplo…

jmeter打开文件报异常无法打开

1、问题现象&#xff1a; 报错部分内容&#xff1a; java.desktop does not export sun.awt.shell to unnamed module 0x78047b92 [in thread "AWT-EventQueue-0"] 报错部分内容&#xff1a; kg.apc.jmeter.reporters.LoadosophiaUploaderGui java.lang.reflect.Invo…

feign设置超时时间

feign设置超时时间 feign的 本质是 调用 http请求&#xff0c;如果不设置超时时间&#xff0c;请求长时间连接着&#xff0c;占用系统资源&#xff0c;影响用户体验。 feign设置超时时间&#xff0c;可以通过 Request.Options 来设置。 FeignClientFactoryBean &#xff1a;…

docker小白第十四天之Portainer与CIG

Portainer简介 Portainer是一款轻量级的应用&#xff0c;它提供了图形化界面&#xff0c;用于方便地管理Docker环境&#xff0c;包括单机环境和集群环境。 Portainer命令安装 # 一个容器可以同时起多个-p端口&#xff0c;restartalways表示随时在线&#xff0c;重启机器后也…

5 Redis主从集群

文章目录 Redis主从集群1.1主从集群搭建1.1.1 伪集群搭建与配置1.1.2 分级管理1.1.3 容灾冷处理 1.2主从复制原理1.2.1 主从复制过程1.2.2 数据同步演变过程 2.1 哨兵机制实现2.1.1 简介2.2.2 Redis 高可用集群搭建2.2.3 Redis 高可用集群的启动2.2.4 Sentinel 优化配置 3.1 哨…