C++函数模板和类模板

news2025/1/17 3:57:53

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);           //显示的指定类型为int
	DEMO1<int, char> demo1();    //显示的指定类型分别为int,char
	cout << "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;
}

当子类与父类都是模板类时,继承时也必须在子类里实例化父类的类型参数,值得注意的是,此时实例化的类型参数可以使用子类的模板类型。即让父类与子类在实例化后拥有一样的具体类型。当然也可以使用其它的具体类型。

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

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

相关文章

【网络安全】跨站脚本(xss)攻击

跨站点脚本&#xff08;也称为 XSS&#xff09;是一种 Web 安全漏洞&#xff0c;允许攻击者破坏用户与易受攻击的应用程序的交互。它允许攻击者绕过同源策略&#xff0c;该策略旨在将不同的网站彼此隔离。跨站点脚本漏洞通常允许攻击者伪装成受害者用户&#xff0c;执行用户能够…

「UG/NX」Block UI 选择特征SelectFeature

✨博客主页何曾参静谧的博客📌文章专栏「UG/NX」BlockUI集合📚全部专栏「UG/NX」NX二次开发「UG/NX」BlockUI集合「VS」Visual Studio「QT」QT5程序设计「C/C+&#

【IMX6ULL驱动开发学习】08.马达驱动实战:驱动编写、手动注册平台设备和设备树添加节点信息

目录 一、使用设备树 1.1 修改设备树流程 二、手动创建平台设备 三、总结&#xff08;附驱动程序&#xff09; 前情提要&#xff1a;​​​​​​​【IMX6ULL驱动开发学习】07.驱动程序分离的思想之平台总线设备驱动模型和设备树_阿龙还在写代码的博客-CSDN博客 手动注册…

SpringjDBCTemplate_spring25

1、首先导入两个包&#xff0c;里面有模板 2、transtion事务 jDbc操作对象&#xff0c;底层默认的是事务&#xff1a; 3、我们java一般对实体类进行操作。 4、第一步写好坐标。 创建一个Account表 数据修改用update 数据进去了

音频转换工具哪个好用?能解决音频格式转换问题吗?

大千世界中的语言自然存在差异&#xff0c;不同的音频格式也有着各自的方言&#xff0c;有时候我们需要一位翻译官来帮助我们更好地欣赏这些美妙的音符。幸运的是&#xff0c;现代的科技可以让音频格式转换变得轻而易举&#xff0c;就像是在不同乐章之间穿越。无论是将古典的FL…

arm:day6

实现UART通信&#xff1a; 1.键盘输入一个字符a,串口工具显示b 2.键盘输入一个字符串"nihao",串口工具显示"nihao" uart.h #ifndef __UART4_H__ #define __UART4_H__#include "stm32mp1xx_uart.h" #include "stm32mp1xx_gpio.h" #in…

Kubernetes的endpoint

简介 Kubernetes的endpoint&#xff08;终结点&#xff09;是用于将服务绑定到集群中其他组件的网络地址。Endpoint为服务提供了一个稳定的虚拟IP地址&#xff0c;它会负责将流量从Service路由到后端Pod。 下面是使用Kubernetes的endpoint的详细步骤&#xff1a; 创建一个Se…

超声波传感器(HC-SR04)按时序图手撕驱动

目录 1、简介 2、传感器介绍 2.1 引脚介绍 2.2 时序图介绍 3、 需求与接线 3.1 任务需求 3.2 接线 4、Cubemax配置 4.1 SYS配置 4.2 RCC配置 4.3 时钟树配置 4.4 GPIO初始化 4.5 定时器配置 4.6 生成代码 5、 keil端代码编写 5.1 微妙函数封装 5.2 超声波驱动封装…

机器学习---线性判别分析

1. 基本思想 线性判别分析(Linear Discriminant Analysis, LDA)&#xff0c;也叫做 Fisher 线性判别(Fisher Linear Discriminant ,FLD)&#xff0c;是模式识别的经典算法&#xff0c;1936年由Ronald Fisher⾸次提出&#xff0c;并在1996年由 Belhumeur引⼊模式识别和⼈⼯智能…

【探索C++】用实例教你理解面向对象编程(看不懂打我版)

(꒪ꇴ꒪ )&#xff0c;Hello我是祐言QAQ我的博客主页&#xff1a;C/C语言&#xff0c;Linux基础&#xff0c;ARM开发板&#xff0c;软件配置等领域博主&#x1f30d;快上&#x1f698;&#xff0c;一起学习&#xff0c;让我们成为一个强大的攻城狮&#xff01;送给自己和读者的…

软件系统工具-架构师真题(六)

_____不属于可修改性考虑的内容。&#xff08;2016&#xff09; 可维护性可扩展性结构重构可变性 答案:D 解析&#xff1a; 可修改性指快速较高的性能价格进行系统优化&#xff0c;包括可维护性、可扩展性、结构重组和可移植性四个方面。 软件系统工具中&#xff0c;软件评…

Docker 常规软件安装

1. 总体安装步骤 1. 搜索镜像 search 2. 拉取镜像 pull 3. 查看镜像 images 4. 启动镜像 - 端口映射 run 5. 停止容器 stop 6. 移除容器 rm 2. 安装tomcat 1. 搜索 docker search tomcat 2. 拉取 docker pull tomcat 3. 查看本地镜像 docker images tomcat 4. 创建容器实…

两个List合并、去重、排序

天行健&#xff0c;君子以自强不息&#xff1b;地势坤&#xff0c;君子以厚德载物。 每个人都有惰性&#xff0c;但不断学习是好好生活的根本&#xff0c;共勉&#xff01; 文章均为学习整理笔记&#xff0c;分享记录为主&#xff0c;如有错误请指正&#xff0c;共同学习进步。…

打通应用“壁垒”,数据分类分级结果与安全策略自动匹配

《网络安全法》、《数据安全法》等法律法规&#xff0c;以及各行业各领域与数据安全相关的标准规范&#xff0c;几乎都涉及对数据进行分类分级保护的要求。数据安全始于分类分级&#xff0c;已成为毫无疑问的行业共识。 但现实中不少用户却止步在分类分级工作&#xff0c;“如…

linux虚拟机中各服务端口及配置文件路径

查询端口状况命令&#xff1a; netstat -an| grep 端口号 查询服务状态&#xff08;服务是否开启&#xff09;命令&#xff1a;systemctl status 服务名 开启服务命令&#xff1a;systemctl start 服务名 21端口&#xff1a;FTP 文件传输服务 22端口&#xff1a;SSH协议、…

无涯教程-Perl - wait函数

描述 该函数等待子进程终止,返回已故进程的进程ID。进程的退出状态包含在$?中。 语法 以下是此函数的简单语法- wait返回值 如果没有子进程,则此函数返回-1,否则将显示已故进程的进程ID Perl 中的 wait函数 - 无涯教程网无涯教程网提供描述该函数等待子进程终止,返回已故…

怎么把pdf压缩到5m以内?压缩办法非常多

怎么把pdf压缩到5m以内&#xff1f;PDF文件是我们办公过程中较为常用的文件格式&#xff0c;PDF文件所包含的内容通常较多&#xff0c;比如文本、图像以及音视频等等。这样的话&#xff0c;PDF文件占用内存也较大。如果需要对PDF文件进行使用、传输、分享等的话&#xff0c;可能…

中路对线发现正在攻防演练中投毒的红队大佬

背景 2023年8月14日晚&#xff0c;墨菲安全实验室发布《首起针对国内金融企业的开源组件投毒攻击事件》NPM投毒事件分析文章&#xff0c;紧接着我们在8月17日监控到一个新的npm投毒组件包 hreport-preview&#xff0c;该投毒组件用来下载木马文件的域名地址竟然是 img.murphys…

【TypeScript】任意类型

前置准备 在 powershell 安装 ts-node 包&#xff0c;可以先借助 xmzs 包切换淘宝镜像&#xff0c;加快安装速度。 在 vscode 终端执行 npm init -y 生成 package.json 文件 然后在 vscode 终端执行 npm i types/node -D 最后就可以在 vscode 终端执行ts-node&#xff0c;直接…

Typora 相对路径保存图片以及 Gitee 无法显示图片

目录 Typora 相对路径保存图片 Gitee 无法显示图片 Typora 相对路径保存图片 Step1&#xff1a;修改 Typora 的偏好设置 自动在当前目录创建名为 "./${filename}.assets" 的文件夹粘贴图片到 md 中时&#xff0c;图片会自动另存到 "./${filename}.assets&qu…