C++: 模板初阶

news2024/11/13 11:23:51

文章目录

  • 一. 泛型编程
  • 二. 函数模板
    • 函数模板的原理
    • 函数模板的实例化
      • 隐式实例化: 让编译器根据实参推演模板参数的实际类型
      • 显示实例化: 在函数名后的<>中制定模板参数的世纪类型
    • 模板参数的匹配原则
  • 三. 类模板
    • 类模板的定义格式
    • 类模板的实例化

一. 泛型编程

如何实现一个通用的交换函数呢? 参数是不同类型的数据.

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++提供了模板的语法, 给这个"模具"中填充不同的类型, 就可以生成具体类型的代码

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

在这里插入图片描述

二. 函数模板

针对上面 Swap 函数有多种类型参数的情况, 可以定义一个通用的函数模板(function template), 而不是为每个类型都定义一个新函数.

一个函数模板就是一个公式, 可用来生成针对特定类型的函数版本. Swap 的模板版本可能像下面这样:

template <typename T>     // 模板定义格式:  template<typename T1, typename T2, ... , typename Tn>
void Swap(T &left, T &right)
{
  T temp = left;
  left = right;
  right = temp;
}

模板定义以关键字 template 开始, 后面跟一个模板参数列表, 这是一个用都号分割的一个或多个模板参数的列表, 用小于号 < 和 大于号 > 包围起来.

在模板定义中, 模板参数列表不能为空.

模板参数列表的作用很像函数参数列表.
函数参数列表定义了形参对象, 模板参数列表定义了类型.

模板参数表示类或函数定义中用到的类型或值. 当使用模板的时候, 显示或隐式地制定模板实参, 将其绑定到模板参数上.

比如上述的 Swap 函数声明了两个名为 T 的类型参数, T 表示一个类型, T 的实际类型则在编译时根据传入的参数确定.

对于不同的参数类型, 最终调用的函数参数类型也不同

在这里插入图片描述


函数模板的原理

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

在这里插入图片描述

在编译器编译阶段, 对于函数模板的使用, 编译器需要根据传入的实参类型来推演生成对应类型的函数以供调用.

例如: 当用 double 类型使用函数模板时, 编译器通过对实参类型的推演, 将 T 确定为 double 类型, 然后产生一份专门处理 double 类型的代码

注意 函数模板的模板参数类型可以写 typename, 也可以写 class (不可以写 struct).

函数模板的实例化

用不同类型的参数使用函数模板时, 称为函数模板的实例化.

在这里插入图片描述

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

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

int main()
{
  int a1 = 10, b1 = 20;
  Add(a1, b1);    // 推演出模板参数类型为 int

  double a2 = 1.1, b2 = 2.2;
  Add(a2, b2);    // 推演出模板参数类型为 double

  Add(a1, b2);    // 推演失败

  return 0;
}

前两个可以通过编译, 函数模板类型相同, 编译器可以推演出函数模板的类型.

第三个传入了一个 int 类型和一个 double 类型, 编译器推演模板参数类型失败.

在这里插入图片描述

因为定义模板函数的时候, 规定了模板函数参数只有一个类型 T, 传入两个类型, 编译器不能确定应该是将 T 演绎成哪个类型.

在模板中, 编译器不会进行类型转换操作, 一旦转换出问题, 编译器就会背黑锅, 所以不会隐式转换.

有两种处理方式:

  1. 用户自己进行强化类型转换
  2. 使用显示实例化
Add(a1, (int)b2);   // 用户自己强制类型转换

显示实例化: 在函数名后的<>中制定模板参数的世纪类型

int main()
{
  int a1 = 10;
  double b = 20.0;

  Add<int> (a1, b2);  // 显示实例化

  return 0;
}

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


如果函数参数没有模板参数, 那么就无法使用隐式实例化, 只能进行显示实例化.

// 申请一个T类型十个元素的数组并返回
template <typename T>
T *f()
{
  T *p = new T[10];
  return p;
}

int main()
{
  int *p1 = f<int>();
  double *p2 = f<double>();

  return 0;
}

模板参数的匹配原则

  1. 一个非模板函数和一个同名的函数模板同时存在, 而且该函数模板可以被实例化为这个非模板函数
// 专门处理 int 类型的加法函数
int Add(const int &left, const int &right)
{
  cout << "(int) Add" << endl;
  return left + right;
}

// 通用加法函数
template <typename T>
T Add(const T &left, const T &right)
{
  cout << "(template) Add" << endl;
  return left + right;
}

int main()
{
  Add(1, 2);          // 有现成的优先用现成的
  Add<int>(1, 2);     // 如果指定显示实例化, 用函数模板
}

在这里插入图片描述


  1. 对于非模板函数和同名函数, 如果其他条件爱你都相同, 在调用时会优先调用非模板函数而不会从该模板产生一个实例. 如果模板可以产生一个具有更好匹配的函数, 那么将选择模板.
// 专门处理 int 类型的加法函数
int Add(const int &left, const int &right)
{
  cout << "(int) Add" << endl;
  return left + right;
}

// 通用加法函数
template <typename T1, typename T2>
T1 Add(const T1 &left, const T2 &right)
{
  cout << "(template) Add" << endl;
  return left + right;
}

int main()
{
  Add(1, 2);          // 与非函数模板完全匹配, 不需要函数模板实例化
  Add(1, 2.0);        // 有更合适的不会进行隐式类型转换调用非函数模板, 而会直接调用函数模板实例化的实例
}

在这里插入图片描述


  1. 函数模板不允许自动类型转换, 而普通函数可以进行自动类型转换
// 专门处理 int 类型的加法函数
int Add(const int &left, const int &right)
{
  cout << "(int) Add" << endl;
  return left + right;
}

// 通用加法函数
template <typename T>
T Add(const T &left, const T &right)
{
  cout << "(template) Add" << endl;
  return left + right;
}

int main()
{
  Add(1, 2.0); // 函数模板不能进行自动类型转换, 只能调用普通函数, 传参的时候进行自动类型转化
}

在这里插入图片描述


总结

  1. 优先调用现有的普通函数
  2. 没有函数模板, 普通函数参数可以自动类型转换的, 使用普通函数.
  3. 可以通过函数模板实例化更合适的函数, 哪怕普通函数可以自动类型转换, 也用函数模板.

三. 类模板

以前写数据结构的时候, 通常会用 typedef 重命名数据结构内数据的类型.
例如 Stack 中用 typedef int STDataType, 制定 Stack 中的数据类型是 int 类型的.

但是, 这样只能保证一次只能用 int, 如果需要用到 double 的, 需要再重新写一份.

有了类模板就能很好的解决这个问题.

类模板的定义格式

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

例如: Stack

template <class T>
class Stack
{
public:
  Stack(size_t capacity = 10)
      : _array(new T[capacity])
      , _capacity(capacity)
      , _top(0)
  {}

  ~Stack();
  
  void push(const T &data);
  //...
private:
  T *_array;
  int _capacity;
  int _top;
};

// 类外定义成员函数必须要加模板参数列表
template <class T>
Stack<T>::~Stack()
{
  delete[](_array);
  _capacity = _top = 0;
}

类外定义成员函数, 必须要加上模板参数列表声明, 并且指定类域时必须加上模板参数列表.

同时, 类模板成员函数的声明与定义必须放在同一个文件中, 否则编译器会链接失败.

类模板的实例化

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

Stack<int> s1;
Stack<double> s2;

普通类的类名即是类型, 而类模板的类名不是类型, 类名<数据类型> 才是整个类的类型.

Stack<int>Stack<double> 不是同一个类, 他们只是同一类模板显示实例化生成的不同类.

本章完.

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

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

相关文章

汽车FMCW毫米波雷达信号处理流程(推荐---基础详细---清楚的讲解了雷达的过程---强烈推荐)------假设每个Chirp采集M个样本点

毫米波雷达在进行多目标检测时,TX发射一个Chirp,在不同距离下RX会接收到多个反射Chirp信号(仅以单个chirp为例)。 雷达通过接收不同物体的发射信号,并转为IF信号,利用傅里叶变换将产生一个具有不同的分离峰值的频谱,每个峰值表示在特定距离处存在物体。 请问,这种多目标…

《持续交付:发布可靠软件的系统方法》- 读书笔记(十四)

持续交付&#xff1a;发布可靠软件的系统方法&#xff08;十四&#xff09; 第 14 章 版本控制进阶14.1 引言14.2 版本控制的历史14.2.1 CVS14.2.2 SVN14.2.3 商业版本控制系统14.2.4 放弃悲观锁 14.3 分支与合并14.3.1 合并14.3.2 分支、流和持续集成 14.4 DVCS14.4.1 什么是 …

云课五分钟-04一段代码学习-大模型分析C++

前篇&#xff1a; 云课五分钟-03第一个开源游戏复现-贪吃蛇 经过01-03&#xff0c;基本了解云课最大的优势之一就是快速复现&#xff08;部署&#xff09;。 视频&#xff1a; 云课五分钟-04一段代码学习-大模型分析C AIGC大模型时代&#xff0c;学习编程语言的方式&#xf…

腾讯待办停止运营怎么办?导出的ics文件数据怎么打开查看

待办提醒类工具是日常办公及生活中必不可少的工具&#xff0c;使用待办提醒类工具可以记录很多容易忘记的事情&#xff0c;其可以帮助大家轻松管理各项事务和提高办事的效率。而随着工作的不断变动&#xff0c;大家选择待办提醒类工具也会不断的发生改变。 比如就拿我自己的使…

2023测试工程师做哪些准备,才能从众人中脱颖而出,不看后悔10年

最近&#xff0c;裁员的声音此起披伏。貌似我们只有努力奔跑&#xff0c;这一块带有命运诅咒的“石头”才不会轻易的落到我们的头上。 在不是金三银四、金九银十的求职旺季外&#xff0c;还会有机会吗&#xff1f;我想&#xff0c;对于有能力的人来说&#xff0c;任何时候都可…

(三)什么是Vite——Vite 主体流程(运行npm run dev后发生了什么?)

什么是vite系列目录: &#xff08;一&#xff09;什么是Vite——vite介绍与使用-CSDN博客 &#xff08;二&#xff09;什么是Vite——Vite 和 Webpack 区别&#xff08;冷启动&#xff09;-CSDN博客 &#xff08;三&#xff09;什么是Vite——Vite 主体流程(运行npm run dev…

慧眼APP开发项目

目录 第1章 项目概述... 3 第2章 可行性分析... 4 2.1 技术可行性... 4 2.2 数据可行性... 4 2.3 应用可行性... 4 第3章 环境搭建... 5 3.1 模型搭建... 5 3.1.1 安装PaddleDetection. 5 3.1.2 自定义数据集... 5 3.2 APP搭建... 5 3.2.1 安装Android Studio. 5 3.…

C++入门(1)—命名空间、缺省参数

目录 一、什么是C 1、C关键字(C98) 2、C兼容C 二、C程序预处理指令 三、命名空间 1、命名冲突 第一种&#xff1a; 第二种&#xff1a; 2、域作用限定符 3、实现命名空间 4、命名空间冲突 5、访问命名空间 6、命名空间“std” 四、输入输出 1、定义 2、自动识…

Python开源项目DifFace——人脸重建(Face Restoration),模糊清晰、划痕修复及黑白上色的实践

无论是自己、家人或是朋友、客户的照片&#xff0c;免不了有些是黑白的、被污损的、模糊的&#xff0c;总想着修复一下。作为一个程序员 或者 程序员的家属&#xff0c;当然都有责任满足他们的需求、实现他们的想法。除了这个&#xff0c;学习了本文的成果&#xff0c;或许你还…

如何在面试中胜出?接口自动化面试题安排上

&#x1f4e2;专注于分享软件测试干货内容&#xff0c;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如有错误敬请指正&#xff01;&#x1f4e2;交流讨论&#xff1a;欢迎加入我们一起学习&#xff01;&#x1f4e2;资源分享&#xff1a;耗时200小时精选的「软件测试」资…

Godot4.1 GDExtension 配置VisualStudio方法梳理以及快捷配置工具

写在最前 本篇教程基于之前教程&#xff0c;并且默认为Windows10&#xff0c;64位&#xff0c;Godot版本4.1.3如果遇到任何问题&#xff0c;欢迎及时提出&#xff0c;如果配置成功了请点个赞&#xff0c;球球啦。 之前教程 https://blog.csdn.net/qq_31805591/article/detai…

天机学堂-1、项目搭建,微服务架构设计

1.学习背景 各位同学大家好&#xff0c;经过前面的学习我们已经掌握了《微服务架构》的核心技术栈。相信大家也体会到了微服务架构相对于项目一的单体架构要复杂很多&#xff0c;你的脑袋里也会有很多的问号&#xff1a; 微服务架构该如何拆分&#xff1f; 到了公司中我需要自…

8.查询数据

一、单表查询 MySQL从数据表中查询数据的基本语为SELECT语。SELECT语的基本格式是: SELECT {* | <字段列名>} [ FROM <表 1>, <表 2>… [WHERE <表达式> [GROUP BY <group by definition> [HAVING <expression> [{<operator>…

Linux网络应用层协议之http/https

文章目录 目录 一、http协议 1.URL 2.http协议格式 3.http的方法 4.http的状态码 5.http常见header 6.实现一个http服务器 二、https协议 1.加密 2.为什么要加密 3.常见的加密方式 对称加密 非对称加密 4.https的工作过程探究 方案1 只使用对称加密 方案2 只使…

类似于推箱子的小游戏 寻找 最短路径

实现效果如下 类似 推箱子小游戏 的变种 C/C版本 BFS最短路径 黑色代表墙壁 不能越过 蓝色代表HOME点 灰色代表要找的小箱子 绿色代表路径 最终目标是将灰色的小箱子移动到蓝色的HOME点 需要两次搜索 第一次是 出发点到灰色小箱子 第二次是灰色小箱子到蓝色HOME点 BF…

python爬取网站数据,作为后端数据

一. 内容简介 python爬取网站数据&#xff0c;作为后端数据 二. 软件环境 2.1vsCode 2.2Anaconda version: conda 22.9.0 2.3代码 链接&#xff1a; 三.主要流程 3.1 通过urllib请求网站 里面用的所有的包 ! pip install lxml ! pip install selenium ! pip install…

100G.的DDoS高防够用吗?

很多人以为100G的DDoS防御已经足够了&#xff0c;但殊不知DDoS攻击大小也是需要分行业类型的&#xff0c;比如游戏、金融、影视、电商甚至ZF或者行业龙头等等行业类型&#xff0c;都是大型DDoS攻击的重灾区&#xff0c;别说100G防御&#xff0c;就算300G防御服务器也不一定够用…

开启核磁数据处理新篇章-MestReNova(MNOVA14)助您轻松解读科学界密码

在科学研究领域&#xff0c;核磁共振&#xff08;NMR&#xff09;技术被广泛应用于分析和解读化学物质的结构和性质。而MestReNova&#xff08;MNOVA14&#xff09;作为一款专业的核磁数据处理软件&#xff0c;凭借其强大的功能和易用性&#xff0c;已成为众多科研人员的首选工…

用这个平台制作电子杂志,,还能实时分享,太方便啦!

在我们看电子杂志的时候&#xff0c;总会觉得图文效果有点枯燥&#xff0c;如果能做出翻页书效果的电子杂志&#xff0c;还给人一种身临其境的真实翻书感就好了。 其实制作这种翻页电子杂志很简单&#xff0c;不需要下载安装任何软件&#xff0c;只需登录FLBOOK这个平台 &…

SpringCloud微服务:Nacos的下载和配置

目录 Nacos的下载 Nacos的配置 Nacos的下载 nacos压缩包&#xff0c;点击下载百度网盘为您提供文件的网络备份、同步和分享服务。空间大、速度快、安全稳固&#xff0c;支持教育网加速&#xff0c;支持手机端。注册使用百度网盘即可享受免费存储空间https://pan.baidu.com/…