c++阶梯之模板初阶

1. 泛型编程

void Swap(int& x, int& y)
{int tmp = x;x = y;y = tmp;
}void Swap(double& x, double& y)
{double tmp = x;x = y;y = tmp;
}void Swap(char& x, char& y)
{char tmp = x;x = y;y = tmp;
}
int main()
{int a = 10, b = 20;double c = 1.1, d = 2.2;char m = '#', n = '*';Swap(a, b);Swap(c, d);Swap(m, n);return 0;
}

我们看这段代码,使用c++的函数重载技术实现了不同类型的交换。但这段代码未免太过冗杂和繁琐,我们写了很多近似重复的东西。

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

那么我们能不能写一个模具,让各个类型的变量都能复用它呢?这就是我们这节要学的泛型编程之模板。就像下面这样,给出一个模具,可以做出各种模型。

如果在C++中,也能够存在这样一个模具,通过给这个模具中填充不同材料(类型),来获得不同材料的铸件(即生成具体类型的代码),那将会节省许多精力。

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

模板分为函数模板与类模板。


 

2. 函数模板 

2.1 概念

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

2.2 格式
 

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

譬如我们将上面的Swap函数模板化。

template<class T>
void Swap(T& x, T& y)
{T tmp = x;x = y;y = tmp;
}int main()
{int a = 10, b = 20;double c = 1.1, d = 2.2;char m = '#', n = '*';Swap(a, b);Swap(c, d);Swap(m, n);cout << a << "  " << b << endl;cout << c << "  " << d << endl;cout << m << "  " << n << endl;return 0;
}

 

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

 2.3 原理

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

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

2.4 函数模板的实例化

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

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

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

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

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

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

2.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函
数
}

定义了一个Add函数的模板,有两个模板参数类型,对于模板函数而言恰好,但对于非函数模板类型而言,需要隐式类型转换,明显模板函数更加匹配。 

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

3. 类模板

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

3.2 类模板的实例化

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

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

范例

#include<iostream>
using namespace std;
template<class T>
class Stack
{
public:Stack():_a(new T[10]), _size(0), _capacity(10){}void push(const T& x);void STPop();T STTop();~Stack();
private:T* _a;int _capacity;int _size;
};template<class T>
Stack<T>::~Stack()
{delete[] _a;_size = 0;_capacity = 0;
}
template<class T>
void Stack<T>::push(const T& x)
{_a[_size++] = x;
}
template<class T>
void Stack<T>::STPop()
{_size--;}
template<class T>
T Stack<T>::STTop()
{return _a[_size - 1];
}
int main()
{Stack<int> s1;s1.push(1);s1.push(2);s1.push(3);cout << s1.STTop() << endl;s1.STPop();cout << s1.STTop() << endl;s1.STPop();cout << s1.STTop() << endl;s1.STPop();return 0;
}

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

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

相关文章

《Spring Security 简易速速上手小册》第7章 REST API 与微服务安全(2024 最新版)

文章目录 7.1 保护 REST API7.1.1 基础知识详解7.1.2 重点案例&#xff1a;使用 JWT 进行身份验证和授权案例 Demo 7.1.3 拓展案例 1&#xff1a;API 密钥认证案例 Demo测试API密钥认证 7.1.4 拓展案例 2&#xff1a;使用 OAuth2 保护 API案例 Demo测试 OAuth2 保护的 API 7.2 …

读书笔记-三国演义-夏侯惇

夏侯惇&#xff08;公元夏侯惇&#xff08;公元190年-公元220年&#xff09;&#xff0c;字元让&#xff0c;沛国谯县&#xff08;今安徽亳州市&#xff09;人&#xff0c;是中国东汉末年至三国时期魏国重要将领之一。他是曹操麾下的得力将领&#xff0c;以勇猛忠诚而闻名于世。…

linux安装matlab获取许可证

1.点击许可证 2. 3. 4. 4.主机ID 打开linux输入 /sbin/ifconfigether后边的就是 6.计算机登录名 打开linux输入 whoami7. 8. 9.

局域网https自签名教程

们的客户是在内网环境里面&#xff0c;所以就只能用自签名证书来搞&#xff0c;我一想这还不容易&#xff0c;就迅速的百度了一下随便找了个文章开始照猫画虎&#xff0c;很快就弄完了&#xff0c;但是弄完后发现还是有问题&#xff0c;而且https 还是报不安全&#xff0c; 1、…

(规划)24届春招和25届暑假实习路线准备规划

春招&&暑假实习&#xff1a; 1.八股&#xff1a; 可以去一些八股网站上面进行阅读。 2.项目&#xff1a;至少准备1-2个项目&#xff0c;可以条理清晰的进行项目介绍和难点剖析。 3.算法&#xff1a; hot100 &#xff0c;剑指offer 能刷的很熟&#xff0c;算法关就差…

【R包报错】使用sceasy包转换rds文件与h5ad文件报错PyType_GetFlags

想要将scanpy的h5ad文件转为rds文件&#xff0c;有一个很方便的包sceasy可以使用&#xff0c;安装简单&#xff0c;代码简易。 安装方式 # bioconda package:conda install -c bioconda r-sceasy# R package:devtools::install_github("cellgeni/sceasy")#额外需要…

android系统开发之--ROM编译,repo使用指南

1、检出代码&#xff0c;指定git指定的分支和manifest repo init -u git://xxx/xxx/manifest.git -b <BRANCH> -m <MANIFEST> 这里-m和-b的含义是&#xff1a; 1. 注意到manifest.git本身也是一个git project 2. 所以&#xff0c;-b指定的是使用#1中这个git proj…

我的相关奖项

博士录取证明 名单第53&#xff1a;https://yzb.bupt.edu.cn/content/content.php?p2_2_651 论文链接 第一篇&#xff1a;https://doi.org/10.1186/s13677-022-00373-8 第二篇&#xff1a;https://doi.org/10.1016/j.ipm.2022.103167 第三篇&#xff1a;https://doi.org/10…

Unity 脚本-生命周期常用函数

在Unity中&#xff0c;万物皆是由组件构成的。 右键创建C&#xff03;脚本&#xff0c;拖动脚本到某物体的组件列表。 生命周期相关函数 using System.Collections; using System.Collections.Generic; using UnityEngine;// 必须要继承 MonoBehaviour 才是一个组件 // 类名…

matplotlib——散点图和条形图(python)

散点图 需求 我们获得北京2016年三月和十月每天白天最高气温&#xff0c;我们现在需要找出气温随时间变化的某种规律。 代码 # 导入库 from matplotlib import pyplot as plt import random# 解决中文乱码 import matplotlib matplotlib.rc("font",family"F…

【笔记】-编程语言以及应用领域

C/C 永远不会衰败的语言&#xff0c;适合偏底层&#xff0c;例如&#xff1a;Windows操作系统80%以上都是由C/C完成的&#xff0c;C/C也集成用于写应用层C/S架构的软件 JAVA 是真正的跨平台的语言 “一次编程&#xff0c;到处使用”Java适合应用层的开发&#xff0c;无论是…

Python的错误和异常

文章目录 python的语法错误异常异常处理用户自定义异常定义清理行为预定义的清理行为 python的语法错误 语法错误&#xff08;Syntax Error&#xff09;是指代码不符合Python语言的语法规则。当解释器在执行代码之前对其进行解析时&#xff0c;如果发现代码中有语法错误&#…

领域驱动设计(Domain-Driven Design DDD)——战略设计1

一、概述 随着系统的增长&#xff0c;它会越来越复杂&#xff0c;当我们无法通过分析对象来理解系统的时候&#xff0c;就需要掌握一些操纵和理解大模型的技术了。 最负雄心的企业欲实现一个涵盖所有业务、紧密集成的系统。因大型公司的业务模型巨大且复杂&#xff0c;很难把它…

2403d,d的108版本更改

原文 编译器更改 1,在object.d中给TypeInfo_Class添加了.nameSig字段 2,总是在调用点求值像__FILE__此类关键字 3,现在可按整数数组转换十六进制串 4,添加支持插值式序列 库更改 1,isForwardRange,isBidirectionalRange和isRandomAccessRange现在带可选元素类型 2,std.uni已…

K8s 镜像缓存管理 kube-fledged 认知

写在前面 博文内容为K8s 镜像缓存管理 kube-fledged 认知内容涉及&#xff1a; kube-fledged 简单介绍部署以及基本使用 理解不足小伙伴帮忙指正 不必太纠结于当下&#xff0c;也不必太忧虑未来&#xff0c;当你经历过一些事情的时候&#xff0c;眼前的风景已经和从前不一样了。…

springboot236基于springboot在线课程管理系统的设计与实现

基于SpringBoot在线课程管理系统的设计与实现 摘要 本文首先介绍了在线课程管理系统的现状及开发背景&#xff0c;然后论述了系统的设计目标、系统需求、总体设计方案以及系统的详细设计和实现&#xff0c;最后对在线课程管理系统进行了系统检测并提出了还需要改进的问题。本系…

Spring Data Redis 使用方式

在Java中操作Redis 1. 在Java中操作Redis1.1 Redis的Java客户端1.2 Spring Data Redis使用方式1.2.1 介绍1.2.1 环境搭建1.2.3 操作常见类型数据 1. 在Java中操作Redis 1.1 Redis的Java客户端 Redis 的 Java 客户端很多&#xff0c;常用的几种&#xff1a; JedisLettuceSpri…

关于Axios接口请求超时处理与重试的方法教程

在前端开发中&#xff0c;使用Axios作为HTTP客户端库进行接口请求是非常常见的做法。然而&#xff0c;在实际开发中&#xff0c;我们经常会遇到网络不稳定或服务器响应缓慢导致接口请求超时的情况。为了提高用户体验和程序的稳定性&#xff0c;我们需要实现接口请求超时的处理与…

UnityAPI的学习——Transform类

Transform类继承自Component类&#xff0c;并实现了IEnumberable接口。Transform是GameObject必须拥有得一个组件&#xff0c;用来管理所在GameObject对象的坐标位置、选择角度、和大小缩放。 Transform实现了IEnumberable接口&#xff0c;因此可以在程序中使用foreach()方法快…

echarts vue 动画效果的水球图、波浪图教程

1、安装插件 前提是已经安装了echarts&#xff08;我的版本是4.2.1&#xff09; npm install echarts-liquidfill --save 我安装了3.1.0版本的&#xff0c;结果运行时报错"TypeError: wave.ensureState is not a function" 原因&#xff1a;echarts版本和echarts-l…