C++函数模板和类模板
C++另一种编程思想称为泛型编程,主要利用的技术是模板
C++提供两种模板机制:函数模板和类模板
C++提供了模板(template)编程的概念。所谓模板,实际上是建立一个通用函数或类,
其类内部的类型和函数的形参类型不具体指定, 用一个虚拟的类型来代表。
这种通用的方式称为模板。模板是泛型编程的基础,泛型编程即以一种独立于任何特定类型的方式编写代码。
即:我们提供一个抽象的函数或类,并不具体指定其中数据的类型,而是某个虚拟类型代替。只提供基本的功能。其具体的数据类型,只在其被调用时视具体情况实例化。
函数模板
举个例子
#include <iostream>
#include <stdio.h>using namespace std;template <typename T1,typename T2> //模板函数声明与定义
T2 test(T1 tmp, T2 tmp1) {T2 tmp2 = tmp + tmp1;return tmp2;
}int main(void) {cout << "test(10, 5)=" << test(10, 5) << endl; //调用模板函数,模板函数通过传入的参数自动推导未实例化的类型cout << "test(5,'A')=" << test(5,'A') << endl;cout << "test(10.5, 5.5) =" << test(10.5, 5.5) << endl;system("pause");return 0;
}
函数模板的声明通过关键字template与typename 实现。其中,template告知编译器这是函数模板的声明,typename用来声明虚拟类型。比如你要声明一个模板函数,里面需要两个不同的变量,那么你就需要通过typename声明两个不同的虚拟类型T1,T2。
声明好后,你就可以在函数定义中使用虚拟类型来定义变量,但是要注意,用同一个虚拟类型定义的变量就只能是同种类型,比如用T1定义的变量只能是同种变量,可以是int,也可以是char。这取决于其实例化时被实例化为哪种类型。
C++函数模板注意事项
注意事项:
1、自动类型推导,必须推导出一致的数据类型T,才可以使用
2、模板必须要确定出T的数据类型,才可以使用
using namespace std;
template<class T>
void mySwap(T& a, T& b)
{T temp = a;a = b;b = temp;
}void test01()
{int a = 10;int b = 20;char c = 'c';//1、自动类型推导,必须推导出一致的数据类型T,才可以使用mySwap(a, b);//mySwap(a, c);推导不出一致的T类型cout << "a = " << a << endl;cout << "b = " << b << endl;
}template<class T>
void func()
{cout << "func()的调用" << endl;
}void test02()
{//2、模板必须要确定出T的数据类型,才可以使用func<int>();
}int main() {test01();test02();return 0;
}
模板函数的调用
1)显式类型调用
可以显式的调用模板函数,即在调用时人为地指明虚拟类型的具体类型。
#include <iostream>
#include <stdio.h>using namespace std;template <typename T1,typename T2> //模板函数声明与定义
T2 test(T1 tmp, T2 tmp1) {T2 tmp2 = tmp + tmp1;return tmp2;
}int main(void) {cout << "test(5,'A')=" << test<int,char>(5, 'A') << endl; //<int,char>显式的指明模板的类型system("pause");return 0;
}
2)自动推导
即不指明具体的数据类型,而让编译器根据传入的数据自动推导出数据类型。
#include <iostream>
#include <stdio.h>using namespace std;template <typename T1,typename T2> //模板函数声明与定义
T2 test(T1 tmp, T2 tmp1) {T2 tmp2 = tmp + tmp1;return tmp2;
}int main(void) {cout << "test(5,'A')=" << test(5, 'A') << endl; //自动推导数据类型system("pause");return 0;
}
模板函数与函数重载
熟悉函数重载的人应该会好奇,如果既有模板函数又有同名的普通函数,而且参数列表的参数个数是一样的,那么在主函数中调用同名函数,编译器具体会调用哪一个呢?
下面看一个例子:
#include <iostream>
#include <stdio.h>using namespace std;template <typename T1,typename T2> //模板函数声明与定义
T1 test(T1 tmp, T2 tmp1) {cout << "调用模板函数!" << endl;return (tmp + tmp1);
}int test(int tmp, int tmp1) { //重载的普通函数cout << "调用普通函数!" << endl;return 0;
}int main(void) {char tmp = 'c';int tmp1 = 0;int a = 5;cout << "test(5,'c')=" << test(a, tmp) << endl; cout << "test(5,0)=" << test(a, tmp1) << endl;system("pause");return 0;
}
普通函数的两个参数都是int型,在第一次调用test时第二个参数使用的是char型,调用的是模板函数,第二次使用的是int型,调用的是普通函数。
这是为什么呢?理论上来说,模板函数两个都能匹配,使用。而普通函数也能匹配这两次调用的参数(在C语言中,char型变量是可以作为int型参数使用的)。
这是因为模板函数可以自动推导类型,在第一次调用中,两个类型分别被推导为int型与char型。而普通函数是两个int型,虽然也能使用传入的参数,但模板函数明显能更好的匹配参数列表。
也就是说,如果模板函数实例化后的类型能更好的匹配参数列表的话就使用模板函数。
那么当这两个函数都能完全匹配参数列表的时候呢?通过第二次test的调用结果不难发现,这时候,编译器会调用普通函数。
如果一定要使用模板函数,可以使用<>显式的指定使用模板函数。看下面的例子。
#include <iostream>
#include <stdio.h>using namespace std;template <typename T1,typename T2> //模板函数声明与定义
T1 test(T1 tmp, T2 tmp1) {cout << "调用模板函数!" << endl;return (tmp + tmp1);
}int test(int tmp, int tmp1) { //重载的普通函数cout << "调用普通函数!" << endl;return 0;
}int main(void) {char tmp = 'c';int tmp1 = 0;int a = 5;cout << "test(5,'A')=" << test(a, tmp) << endl; cout << "test<>(5,0)=" << test<>(a, tmp1) << endl; //使用<>显式的调用模板函数system("pause");return 0;
}
类模板
类模板是为了减少重复工作量而出现的一种进制,当一个类的功能类似只是类型不同时,一个通用的类模板可以根据使用者的需要而生成具体类型的类,从而减少功能重复的代码。
类模板作用:建立一个通用类,类中的成员 数据类型可以不具体制定,用一个虚拟的类型来代表
解释:
template–声明创建模板
typename–表明其后面的符号是一种数据类型,可以用class代替
T–通用的数据类型,名称可以替换,通常为大写字母
在类内部定义与声明
#include <iostream>
#include <stdio.h>using namespace std;template <typename T>
class DEMO {public:DEMO(T data) {this->data = data;}~DEMO() {}int GetData() {return this->data;}private:T data;
};template <typename T,typename T1>
class DEMO1 {public:DEMO1() {}~DEMO1();private:T data;T1 ch;
};int main(void) {DEMO<int> demo(5); //显示的指定类型为intDEMO1<int, char> demo1(); //显示的指定类型分别为int,charcout << "data=" << demo.GetData() << endl;system("pause");return 0;
}
类模板的定义与使用使用的是和模板函数一样的关键字。即先声明template,在使用typename声明虚拟类型。
与模板函数不同的是,类模板不能被自动推导出类型,只能显示的指定具体的类型。如上面代码中的 DEMO< int > demo(5),该模板类被显示的指定为int型。
在类外部定义成员函数
在类内部声明成员函数,在类外部定义成员函数时,只要成员函数参数列表中出现了类限定域说明,模板类作为返回类型,模板类作为参数类型,那么就要在成员函数之前声明 template <类型形式参数表>,并且在模板类后加上虚拟参数列表。
#include <iostream>
#include <stdio.h>using namespace std;template <typename T>
class DEMO {public:DEMO(T data);~DEMO();DEMO operator+(int sum);int PrintData(DEMO& demo);private:T data;
};template<typename T> //出现了类限定域说明
DEMO<T>::DEMO(T data)
{this->data = data;
}template<typename T> //出现了类限定域说明
DEMO<T>::~DEMO()
{
}template<typename T> //出现了作为返回值类型的模板类类型
DEMO<T> DEMO<T>::operator+(int sum)
{return *this;
}template<typename T> //出现了作为参数类型的模板类类型
int DEMO<T>::PrintData(DEMO<T>& demo)
{cout << "data=" << demo.data << endl;return 0;
}int main(void) {DEMO<int> demo(5), demo1(15);demo.PrintData(demo1);demo + 5;system("pause");return 0;
}
DEMO< T >中的< T >是虚拟参数列表。
总结来说,只要看到了模板类的类名关键字出现在成员函数参数列表中,就要在成员函数之前声明 template <类型形式参数表>,并且在模板类类名后加上虚拟参数列表。
模板类的继承
分为三种情况。一:子类是模板类,父类是普通类;二:父类是模板类,子类是普通类;三:父类与子类都是模板类。其中第一种情况与两个普通类的继承是一样的。就不说了。
1)父类是模板类,子类是普通类:
#include <iostream>
#include <stdio.h>using namespace std;template <typename T>
class FATHER {public:FATHER(T data) {this->data = data;}~FATHER() {}private:T data;
};class SON:public FATHER<int> { //显示的指明父类的具体类型public:SON(int data):FATHER<int>(data) {this->data = data;}~SON() {}int GetData() {return data;}private:int data;
};int main(void) {SON son(15);cout << "data=" << son.GetData() << endl;system("pause");return 0;
}
子类是一般类,父类是模板类,继承时必须在子类里实例化父类的类型参数。其实这很好理解,因为在子类对象构造之前,会先调用父类的构造函数,父类为模板类,要想实例化,需要有指定的类型。
2)父类与子类都是模板类:
#include <iostream>
#include <stdio.h>using namespace std;template <typename T>
class FATHER {public:FATHER(T data) {this->data = data;}~FATHER() {}private:T data;
};template <typename T1>
class SON:public FATHER<T1> { //使用子类的模板类型传递到父类中,也可以使用具体的类型public:SON(int data):FATHER<int>(data) {this->data = data;}~SON() {}int GetData() {return data;}private:T1 data;
};int main(void) {SON<int> son(15);cout << "data=" << son.GetData() << endl;system("pause");return 0;
}
当子类与父类都是模板类时,继承时也必须在子类里实例化父类的类型参数,值得注意的是,此时实例化的类型参数可以使用子类的模板类型。即让父类与子类在实例化后拥有一样的具体类型。当然也可以使用其它的具体类型。
相关文章:

C++函数模板和类模板
C另一种编程思想称为泛型编程,主要利用的技术是模板 C提供两种模板机制:函数模板和类模板 C提供了模板(template)编程的概念。所谓模板,实际上是建立一个通用函数或类, 其类内部的类型和函数的形参类型不具体指定, 用…...

【Unity】编辑器下查找制定文件下的所有特定资源
需求上很简单,就是在编辑器下,找到某个制定文件下的所有特定资源(UnityEngine.Object)。Unity 没有提供专门的 API,我一开始想在网上搜索代码,发现没有现成可以直接用的。 功能实现本身并不复杂,…...

分布式唯一ID实战
目录 一、UUID二、数据库方式1、数据库生成之简单方式2、数据库生成 - 多台机器和设置步长,解决性能问题3、Leaf-segment 方案实现4、双 buffer 优化5、Leaf高可用容灾 三、基于Redis实现分布式ID四、雪花算法1、雪花算法介绍2、 雪花算法生产环境架构:3…...

el-element日期时间组件限制可选时间范围
<el-date-pickerv-model"formData.meetingTime"type"datetime"value-format"yyyy-MM-dd HH:mm:ss"style"width: 100%"placeholder"请选择日期"clearable:picker-options"pickerOptions"></el-date-picke…...

【李沐】3.3线性回归的简洁实现
1、生成数据集 import numpy as np import torch from torch.utils import data from d2l import torch as d2l true_w torch.tensor([2, -3.4]) # 定义真实权重 true_w,其中 [2, -3.4] 表示两个特征的权重值 true_b 4.2 # 定义真实偏差 true_b,表示…...

Ghost-free High Dynamic Range Imaging withContext-aware Transformer
Abstract 高动态范围(HDR)去鬼算法旨在生成具有真实感细节的无鬼HDR图像。 受感受野局部性的限制,现有的基于CNN的方法在大运动和严重饱和度的情况下容易产生重影伪影和强度畸变。 本文提出了一种新的上下文感知视觉转换器(CA-VIT)用于高动态…...

过来,我告诉你个秘密:送给程序员男友最好的礼物,快教你对象学习磁盘分区啦!小点声哈,别让其他人学会了!
[原文连接:来自给点知识](过来,我告诉你个秘密:送给程序员男友最好的礼物,快教你对象学习磁盘分区啦!小点声哈,别让其他人学会了!) 再唱不出那样的歌曲 听到都会红着脸躲避 虽然会经常忘了我依然爱着你 …...

Cadence+硬件每日学习十个知识点(38)23.8.18 (Cadence的使用,界面介绍)
文章目录 1.Cadence有共享数据库的途径2.Cadence启动3.Cadence界面菜单简介(file、edit、view、place、options)4.Cadence界面的图标简介5.我的下载资源有三本书 1.Cadence有共享数据库的途径 答: AD缺少共享数据库的途径,目前我…...

React Native Expo项目,复制文本到剪切板
装包: npx expo install expo-clipboard import * as Clipboard from expo-clipboardconst handleCopy async (text) > {await Clipboard.setStringAsync(text)Toast.show(复制成功, {duration: 3000,position: Toast.positions.CENTER,})} 参考链接:…...

React源码解析18(5)------ 实现函数组件【修改beginWork和completeWork】
摘要 经过之前的几篇文章,我们实现了基本的jsx,在页面渲染的过程。但是如果是通过函数组件写出来的组件,还是不能渲染到页面上的。 所以这一篇,主要是对之前写得方法进行修改,从而能够显示函数组件,所以现…...

vscode ssh 远程 gdb 调试
一、点运行与调试,生成launch.json 文件 二、点添加配置,选择GDB 三、修改启动程序路径...

云原生 AI 工程化实践之 FasterTransformer 加速 LLM 推理
作者:颜廷帅(瀚廷) 01 背景 OpenAI 在 3 月 15 日发布了备受瞩目的 GPT4,它在司法考试和程序编程领域的惊人表现让大家对大语言模型的热情达到了顶点。人们纷纷议论我们是否已经跨入通用人工智能的时代。与此同时,基…...

PHP酒店点菜管理系统mysql数据库web结构apache计算机软件工程网页wamp
一、源码特点 PHP 酒店点菜管理系统是一套完善的web设计系统,对理解php编程开发语言有帮助,系统具有完整的源代码和数据库,系统主要采用B/S模式开发。 代码下载 https://download.csdn.net/download/qq_41221322/88232051 论文 https://…...

【面试复盘】知乎暑期实习算法工程师二面
来源:投稿 作者:LSC 编辑:学姐 1. 自我介绍 2. 介绍自己的项目 3. 编程题 判断一个链表是不是会文链表class ListNode: def __init__(self, val, nextNone):self.val valself.next nextdef reverse(head):pre Nonep headwhile p ! No…...

内网穿透和服务器+IP 实现公网访问内网的区别
内网穿透和服务器IP 实现公网访问内网的区别在于实现方式和使用场景。 内网穿透(Port Forwarding):内网穿透是一种通过网络技术将公网用户的请求通过中转服务器传输到内网设备的方法。通过在路由器或防火墙上进行配置,将公网请求…...

JAVA权限管理 助力企业精细化运营
在企业的日常经营中,企业人数达到一定数量之后,就需要对企业的层级和部门进行细分,建立企业的树形组织架构。围绕着树形组织架构,企业能够将权限落实到个人,避免企业内部出现管理混乱等情况。权限管理是每个企业管理中…...

金融语言模型:FinGPT
项目简介 FinGPT是一个开源的金融语言模型(LLMs),由FinNLP项目提供。这个项目让对金融领域的自然语言处理(NLP)感兴趣的人们有了一个可以自由尝试的平台,并提供了一个与专有模型相比更容易获取的金融数据。…...

LeetCode--HOT100题(30)
目录 题目描述:24. 两两交换链表中的节点(中等)题目接口解题思路代码 PS: 题目描述:24. 两两交换链表中的节点(中等) 给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节…...

Springboot 实践(3)配置DataSource及创建数据库
前文讲述了利用MyEclipse2019开发工具,创建maven工程、加载springboot、swagger-ui功能。本文讲述创建数据库,为项目配置数据源,实现数据的增删改查服务,并通过swagger-ui界面举例调试服务控制器 创建数据库 项目使用MySQL 8.0.…...

【问题整理】Ubuntu 执行 apt-get install xxx 报错
Ubuntu 执行 apt-get install xxx 报错 一、问题描述: 执行apt-get install fcitx时,报如下错误 grub-pc E: Sub-process /usr/bin/dpkg returned an error code (1)二、解决方法: 尝试修复依赖问题: sudo apt-get -f install这个命令会尝试修复系统…...

Java课题笔记~ SpringBoot简介
1. 入门案例 问题导入 SpringMVC的HelloWord程序大家还记得吗? SpringBoot是由Pivotal团队提供的全新框架,其设计目的是用来简化Spring应用的初始搭建以及开发过程 原生开发SpringMVC程序过程 1.1 入门案例开发步骤 ①:创建新模块&#…...

一种基于springboot、redis的分布式任务引擎的实现(一)
总体思路是,主节点接收到任务请求,将根据任务情况拆分成多个任务块,将任务块标识的主键放入redis。发送redis消息,等待其他节点运行完毕,结束处理。接收到信息的节点注册本节点信息到redis、开启多线程、获取任务块、执…...

基于IDE Eval Resetter延长IntelliJ IDEA等软件试用期的方法(包含新版本软件的操作方法)
本文介绍基于IDE Eval Resetter插件,对集成开发环境IntelliJ IDEA等JetBrains公司下属的多个开发软件,加以试用期延长的方法。 我们这里就以IntelliJ IDEA为例,来介绍这一插件发挥作用的具体方式。不过,需要说明使用IDE Eval Rese…...

RocketMQ消费者可以手动消费但无法主动消费问题,或生成者发送超时
1.大多数是配置问题 修改rocketmq文件夹broker.conf 2.配置与集群IP或本地IPV4一样 重启 在RocketMQ独享实例中支持IPv4和IPv6双栈,主要是通过在网络层面上同时支持IPv4和IPv6协议栈来实现的。RocketMQ的Broker端、Namesrv端和客户端都需要支持IPv4和IPv6协议&…...

【数据库系统】--【2】DBMS架构
DBMS架构 01DBMS架构概述02 DBMS的物理架构03 DBMS的运行和数据架构DBMS的运行架构DBMS的数据架构PostgreSQL的体系结构RMDB的运行架构 04DBMS的逻辑和开发架构DBMS的层次结构DBMS的开发架构DBMS的代码架构 05小结 01DBMS架构概述 02 DBMS的物理架构 数据库系统的体系结构 数据…...

第三章 图论 No.13拓扑排序
文章目录 裸题:1191. 家谱树差分约束拓扑排序:1192. 奖金集合拓扑序:164. 可达性统计差分约束拓扑序:456. 车站分级 拓扑序和DAG有向无环图联系在一起,通常用于最短/长路的线性求解 裸题:1191. 家谱树 119…...

喜报 | 擎创再度入围IDC中国FinTech 50榜单
8月16日,2023年度“IDC中国FinTech 50”榜单正式揭晓,擎创科技继2022年入选该榜单后,再次以创新者姿态成功入选,并以技术赋能业务创新,成为中国金融科技领域创新与活力的重要贡献者。 “IDC中国FinTech 50”旨在评选出…...

【C++ 记忆站】引用
文章目录 一、引用概念二、引用特性1、引用在定义时必须初始化2、一个变量可以有多个引用3、引用一旦引用一个实体,再不能引用其他实体 三、常引用四、使用场景1、做参数1、输出型参数2、大对象传参 2、做返回值1、传值返回2、传引用返回 五、传值、传引用效率比较六…...

Hlang--用Python写个编程语言-变量的实现
文章目录 前言语法规则表示次幂实现变量实现优先级实现步骤解析关键字语法解析解释器总结前言 先前的话,我们终于是把我们整个架子搭起来了,这里重复一下我们的流程,那就是,首先,我们通过解析文本,然后呢遍历文本当中的我们定义的合法关键字,然后呢,把他们封装为一个T…...

多维时序 | MATLAB实现PSO-CNN-BiLSTM多变量时间序列预测
多维时序 | MATLAB实现PSO-CNN-BiLSTM多变量时间序列预测 目录 多维时序 | MATLAB实现PSO-CNN-BiLSTM多变量时间序列预测基本介绍模型特点程序设计参考资料 基本介绍 本次运行测试环境MATLAB2021b,MATLAB实现PSO-CNN-BiLSTM多变量时间序列预测。代码说明:…...