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);           //显示的指定类型为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;
}

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

SpringBoot代理访问本地静态资源400 404

SpringBoot代理访问静态资源400 404 背景&#xff1a;pdf文件上传到linux服务器上&#xff0c;使用SpringBoot代理访问问题&#xff1a;访问过程中可能会出现400、404问题 前提&#xff1a;保证有文件&#xff0c;并且文件路径正确 SpringBoot如何配置静态资源代理&#xff0…

网络综合布线实训室建设方案

一、网络综合布线系统概述 网络综合布线系统是为了满足数据通信需求而设计和建立的一套基础设施。它提供了数据传输、信号传输和电力供应的基础结构&#xff0c;支持各种网络设备和终端设备之间的连接。 网络综合布线系统通常包括以下组成部分&#xff1a; 1&#xff09; 数据…

1.MySQL数据库的基本操作

数据库操作过程&#xff1a; 1.用户在客户端输入 SQL 2.客户端会把 SQL 通过网络发送给服务器 3.服务器执行这个 SQL,把结果返回给客户端 4.客户端收到结果,显示到界面上 数据库的操作 这里的数据库不是代表一个软件&#xff0c;而是代表一个数据集合。 显示当前的数据库 …

PHP基础

PHP&#xff08;外文名:PHP:Hypertext Preprocessor&#xff0c;中文名&#xff1a;“超文本预处理器”&#xff09;是一种免费开源的、创建动态交互性站点的强有力的服务器端脚本语言 <h1>My Name is LiSi!</h1> <script>console.log("This message is…

GuLi商城-前端基础Vue-使用Vue脚手架进行模块化开发

自己亲自实践&#xff1a; mac安装webpack webpack 简介Webpack 是一个非常流行的前端构建工具&#xff0c;它可以将多个模块&#xff08;包括CSS、JavaScript、图片等&#xff09;打包成一个或多个静态资源文件&#xff08;bundle&#xff09;&#xff0c;以便用于部署到生产…

测试人进阶技能:单元测试报告应用指南

为什么需要单元测试 从产品角度而言&#xff0c;常规的功能测试、系统测试都是站在产品局部或全局功能进行测试&#xff0c;能够很好地与用户的需要相结合&#xff0c;但是缺乏了对产品研发细节&#xff08;特别是代码细节的理解&#xff09;。 从测试人员角度而言&#xff0…

无涯教程-TensorFlow - TensorBoard可视化

TensorFlow包含一个可视化工具&#xff0c;称为TensorBoard&#xff0c;它用于分析数据流图&#xff0c;还用于了解机器学习模型。 TensorBoard的重要功能包括查看有关垂直对齐的任何图形的参数和详细信息的不同类型统计的视图。 深度神经网络包括多达36&#xff0c;000个节点…

Java实现CAS的原理

文章目录 1、 什么是CAS2、CAS的原理3、CAS的应用场景4、Java中的CAS实现5、使用AtomicInteger实现线程安全的计数器6、CAS实现原子操作的三大问题6.1、ABA问题6.2、循环时间长6.3、只能保证一个共享变量的原子性 7、总结 1、 什么是CAS CAS&#xff08;Compare and Swap&…

读书笔记-《ON JAVA 中文版》-摘要22[第二十章 泛型-1]

文章目录 第二十章 泛型1. 简单泛型1.1 简单泛型1.2 一个元组类库 2. 泛型接口3. 泛型方法3.1 泛型方法3.2 变长参数和泛型方法 4. 构建复杂模型 第二十章 泛型 普通的类和方法只能使用特定的类型&#xff1a;基本数据类型或类类型。如果编写的代码需要应用于多种类型&#xff…

5G+AI数字化智能工厂建设解决方案PPT

导读&#xff1a;原文《5GAI数字化智能工厂建设解决方案》&#xff08;获取来源见文尾&#xff09;&#xff0c;本文精选其中精华及架构部分&#xff0c;逻辑清晰、内容完整&#xff0c;为快速形成售前方案提供参考。数字化智能工厂定义 智能基础架构协同框架 - 端、边、云、网…

ARM 作业1

一、思维导图 二、 1. 2. .text 文本段 .globl _start 声明_start:mov r0,#0mov r1,#0fun:cmp r1,#100bhi stopadd r0,r0,r1add r1,r1,#1b fun stop:b stop .end

【3Ds Max】车削命令的简单使用(以制作花瓶为例)

简介 在3ds Max中&#xff0c;"车削"&#xff08;Lathe&#xff09;是一种建模命令&#xff0c;用于创建围绕轴线旋转的几何形状。通过车削命令&#xff0c;您可以将一个闭合的平面或曲线几何形状旋转&#xff0c;从而生成一个立体对象。这种方法常用于创建圆柱体、…

原生微信小程序自定义picker多列选择器:picker写法用法

前言: 最近用原生微信小程序写法写医疗相关项目微信小程序&#xff0c;在编辑个人资料的时候&#xff0c;需要很多选择器&#xff0c;比如城市地区选择器&#xff0c;职业职称选择器&#xff0c;科室选择器&#xff0c;学校选择器&#xff0c;学历选择器&#xff0c;年份日期选…

YOLOv5改进系列(21)——替换主干网络之RepViT(清华 ICCV 2023|最新开源移动端ViT)

【YOLOv5改进系列】前期回顾: YOLOv5改进系列(0)——重要性能指标与训练结果评价及分析 YOLOv5改进系列(1)——添加SE注意力机制 YOLOv5改进系列(2

基于YOLOv8模型的五类动物目标检测系统(PyTorch+Pyside6+YOLOv8模型)

摘要&#xff1a;基于YOLOv8模型的五类动物目标检测系统可用于日常生活中检测与定位动物目标&#xff08;狼、鹿、猪、兔和浣熊&#xff09;&#xff0c;利用深度学习算法可实现图片、视频、摄像头等方式的目标检测&#xff0c;另外本系统还支持图片、视频等格式的结果可视化与…

如何区分闰年与平年

首先要明白 地球绕太阳运行周期为365天5小时48分46秒&#xff08;合365.24219天&#xff09;&#xff0c;即一回归年&#xff08;tropical year&#xff09;。公历的平年只有365日&#xff0c;比回归年短约0.2422 日&#xff0c;每四年累积约一天&#xff0c;把这一天加于2月末…

CentOS7源码安装MySQL详细教程

&#x1f60a; 作者&#xff1a; Eric &#x1f496; 主页&#xff1a; https://blog.csdn.net/weixin_47316183?typeblog &#x1f389; 主题&#xff1a;CentOS7源码安装MySQL详细教程 ⏱️ 创作时间&#xff1a; 2023年08月014日 文章目录 1、安装的四种方式2、源码安装…

机器学习基础之《分类算法(3)—模型选择与调优》

作用是如何选择出最好的K值 一、什么是交叉验证&#xff08;cross validation&#xff09; 1、定义 交叉验证&#xff1a;将拿到的训练数据&#xff0c;分为训练和验证集。以下图为例&#xff1a;将数据分成5份&#xff0c;其中一份作为验证集。然后经过5次(组)的测试&#x…

nginx php-fpm安装配置

nginx php-fpm安装配置 nginx本身不能处理PHP&#xff0c;它只是个web服务器&#xff0c;当接收到请求后&#xff0c;如果是php请求&#xff0c;则发给php解释器处理&#xff0c;并把结果返回给客户端。 nginx一般是把请求发fastcgi管理进程处理&#xff0c;fascgi管理进程选…

musl libc ldso 动态加载研究笔记:02

前言 本篇继续研究 musl libc ldso 的动态加载过程中遇到的关键性的概念&#xff1a;到底要加载ELF 文件的哪些内容到 内存 当前如果遇到 ELF 动态加载&#xff0c;当前系统需要有【文件系统】&#xff0c;并且有较大的内存&#xff0c;因为 ELF 文件是无法直接运行的&#xf…