C++模板(初阶)

news2024/11/24 2:42:32

本章主要讲解三个部分:泛型编程、函数模板、类模板


目录

泛型编程

函数模板

函数模板概念

函数模板的格式

函数模板的原理

函数模板的实例化

隐式实例化

显式实例化

模板参数的匹配原则

 类模板


泛型编程

先来大致说一下什么是泛型编程.

在计算机程序设计领域,为了避免因数据类型的不同,而被迫重复编写大量相同业务逻辑的代码,人们发展的泛型及泛型编程技术.

接下来说一下泛型编程(模板)到底有什么用途或意义.

先来看C语言中这样的问题:

void Swap(int& left, int& right)
{
    int temp = left;
    left = right;
    right = temp;
}
int main()
{
    int x = 4;
    int y = 6;
    Swap(x, y);
}

这里没有问题,将x和y进行了交换,类型也是整型的.

但是如果有两个double类型数据进行交换呢?

此时的Swap显然不可以,因为它的参数数据类型是int,与实参不匹配.

这里可能会有人提出这样的一种解决办法:

既然C++支持了函数重载,那么我们直接再写一份形参为double类型的不就可以了吗?没有问题,我们写一份:

void Swap(double& left, double& right)
{
    double temp = left;
    left = right;
    right = temp;
}

这样其实并没有解决本质,当后面数据类型是char,float,short等等甚至是自定义类型该怎么交换呢?不可能一个个写出来吧,那也会太麻烦了.

那能否告诉编译器一个模子,让编译器根据不同的类型利用该模子来生成代码呢?

于是C++便提出了模板概念.

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

这里再次说一下利用函数重载的一些问题:

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

模板分为函数模板和类模板.

先说函数模板

函数模板

函数模板概念

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

函数模板的格式

这里用到一个新的关键字:template

用法:template<typename T1,typename T2 .....,typename Tn>

返回值类型 函数名(参数列表){}

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

那既然有了模板,我们再来尝试解决一下刚才的问题.

可以先定义一个模板参数,然后再写利用这个参数类型写函数.

//泛型编程 --- 模板
//模板参数(模板类型) --- 类似于函数参数(参数对象)
//typename后面的名字T随便取,Ty,K,V等等都可以,一般是大写字母或单词首字母大写
//T代表一个模板类型(虚拟类型)
template<typename T>
void Swap(T& left, T& right)
{
    T tmp = left;
    left = right;
    right = tmp;
}

这个时候再次进行交换:

int main()
{
    int x = 4;
    int y = 6;
    double a = 1.2;
    double b = 1.5;
    Swap(x, y);
    Swap(a, b);
    cout << x << endl << y << endl;
    cout << a << endl << b << endl;
}

 可以看到无论是int类型还是double类型,都已经完成了交换.

当然把typename改成class也可以.它俩几乎没有任何区别.

那么问题来了,int类型的两个数据交换和double类型的交换是不是调用了同一个函数呢?

其实调用的不是同一个函数,是先进行模板参数的推演,然后再进行推演参数的实例化.

相当于生成了两份数据类型不同代码.

下面将分别讲解这些.

函数模板的原理

经过上面的说明,可以大概的知道:

函数模板的本质是重复的工作交给了机器去完成.

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

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

在编译器看来,double,int和char类型是三个不一样的函数.

可以看到函数的地址确实不同

当然大家可以直接用库里的swap函数,即可以完成所有类型的交换.

还有自己实现Swap注意的一点是,使用模板时不能再使用^实现交换操作,因为^只针对整型有用.

函数模板的实例化

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

隐式实例化

让编译器根据实参推演模板参数的实际类型

上面也说了,编译器根据实参类型然后实例化出对应类型的函数.

如:

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

int main()
{
	Add(1, 2);
	Add(1.5, 2.5);
	return 0;
}

这样都可以编译通过.

第一份被实例化成了int类型

第二份被实例化成了double类型

但是我如果这样写呢?

	Add(1.5, 2);

还能不能编译通过?

答案是编译不通过.

该语句不能通过编译,因为在编译期间,当编译器看到该实例化时,需要推演其实参类型
通过实参a1将T推演为int,通过实参d1将T推演为double类型,但模板参数列表中只有
一个T,
  编译器无法确定此处到底该将T确定为int 或者 double类型而报错
  注意:在模板中,编译器一般不会进行类型转换操作,因为一旦转化出问题,编译器就需要
背黑锅

对于这种问题,当然可以再设置一个模板参数.

但只针对于一个模板参数,这里有两种方法:

1.用户自己来强制转化

2.使用显式实例化

先说第一种;

既然是两种类型,那么我们把它强转一下不就可以了吗?

如下:

	Add((int)1.5, 2);

或者

	Add(1.5, (double)2);

既让两种类型相同即可.

这样编译就没有问题了

显式实例化

在函数名后的<>中指定模板参数的实际类型

原来的写法就可以写成:

	Add<int>(1.1, 2);

或者

	Add<double>(1.1, 2);

这样就相当于提前告诉编译器模板参数的类型了,编译器就不用自己再推演了.

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

注意两个的过程不一样:

用户强制转化是先强制把类型转化成一样的,然后编译器再推导出参数类型,再实例化

显示实例化是实现告诉了编译器参数类型,已经确定好了,不需要再推导,传参时就直接隐式类型转化了.

两个模板参数时,还会有一些值得注意的问题,我们来看一下.

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

int main()
{
	cout << Add(1, 2) << endl;
	cout << Add(1.5, 2) << endl;
	cout << Add(1, 2.5) << endl;
	return 0;
}

先看Add(1,2)

首先T1 和 T2都会被推演为int类型,然后相加,类型还是int,此时函数返回值类型是T1,也是int类型,没有什么问题.

再来看Add(1.5,2)

T1会被推导为double类型,T2会被推导为int类型,相加之后,默认是从小到大提升,既int类型整型提升成double类型,所以结果此时为double类型,正好F返回类型T1也double类型,也没有问题.

Add(1,2.5)

T1会被推导为int类型,T2会被推导为double类型,相加之后,和第二个一样,结果是double类型,但此时返回类型是T1int类型,所以此时需要发生隐式类型转化为int(截断).

模板参数的匹配原则

那么这里会有一个问题:就是一个非模板函数可以和一个同名的函数模板同时存在吗?

答案是可以同时存在的,而且这个函数模板还可以实例化成为这个非模板函数.

这是怎么回事呢?

看下面这段代码:

//专门处理int类型的函数
int Add(int left, int right)
{
	cout << "非模板函数" << endl;
	return left + right;
}
//通用函数
template<class T>
T Add(T left, T right)
{
	cout << "模板函数" << endl;
	return left + right;
}

int main()
{
	cout << Add(1, 2) << endl;
	cout << Add(2.5, 2.5) << endl;

	return 0;
}

我们先运行一下看看结果.

可以看到Add(1,2)没有使用模板函数实例化,而是直接使用了那个非模板函数.

而Add(2.5,2.5)使用了模板函数实例化

这是为什么呢?

因为对于非模板函数和同名函数模板,如果其他条件都相同,在调动时会优先调用非模板函数而
不会从该模板产生出一个实例。如果模板可以产生一个具有更好匹配的函数, 那么将选择模

但是我们一定想用模板函数进行处理呢?

可以进行特化一下,这个后面也会将.

	Add<int>(1, 2);

 

可以看到已经成功调用了模板函数.

 类模板

类模板用的非常多.

先来说之前存在的问题.

比如一个栈,它的每个类里面的类型都是确定的,想同时存在多个不同的数据类型的栈类,必须用不同的类名,拷贝多份相同的代码,正如下所示:

class Stacki
{
private:
	int* _a;
	int size;
	int capacity;
};

class Stackc
{
private:
	char* _a;
	int size;
	int capacity;
};
int main()
{
	Stacki sti;
	Stackc stc;
	return 0;
}

造成代码的冗余

这也是为什么c语言实现不了数据结构的库,因为每个数据类型都不一样.都需要重新写一个.

这个时候模板就发挥了大作用.

先写一下类模板的格式:

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

template<class T>
class Stack
{
public:
	Stack(size_t capacity = 4)
		:_a(nullptr)
		,_capacity(0)
		,_top(0)
	{
		if (capacity > 0)
		{
			_a = new T[capacity];
			_capacity = capacity;
			//_top = 0;
		}
	}
private:
	T* _a;
	size_t _top;
	size_t _capacity;
};

int main()
{
	Stack<int> sti;
	Stack<char> stc;
	Stack<double> stb;
	return 0;
}

这样发现无论什么类型的类,都可以被实例化了.

这里需要注意的问题是:类模板实例化需要在类模板名字后跟<>(显式实例化),然后将实例化的类型放在<>中即可,类模板名字不是真正的类,而实例化的结果才是真正的类。因为如果不传参数的话,编译器就没法推导这个类是什么类型.

2.虽然他们用了同一个类模板,但是Stack<int>,Stack<char>是两个类型.

基本的类模板就到这了.还有一些进阶的模板内容将在后面讲解.

模板这一章就到这了,如果有错误或疑问,欢迎指正或提问哦~

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

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

相关文章

如何安装第三方的Python包?

目录 什么是第三方包 常见的第三方包 如何安装 方法1&#xff1a;命令行 方法2&#xff1a;在IDE中用图形界面操作 什么是第三方包 我们知道&#xff0c;在Python中包就是把一些同类型的功能封装到一处&#xff0c;包含了函数、类、变量等可供外界使用。使用的方法就是在文…

肠道微生物群、营养与长期疾病风险:母婴视角

谷禾健康 怀孕的母亲与体内的胎儿是息息相关的。由于婴儿接触母体微生物群&#xff0c;母亲和孩子之间的微生物联系在怀孕期间形成。而宿主与微生物群的联系在出生后成熟&#xff0c;并进化成为个体生命中最重要的共生关系之一&#xff0c;对响应营养和环境刺激的稳态调节至关重…

【Git】“分支” 如何管理和使用?这一篇就够了~

目录 一、什么是分支&#xff1f; 二、分支的操作 2.1、查看分支 2.2、创建分支 2.3、切换分支 和 修改分支 2.4、合并分支 2.4.1、正常合并 2.4.2、合并冲突 三、创建分支和切换分支的底层原理 一、什么是分支&#xff1f; 在版本控制中&#xff0c;我们可以针对每个任…

单调队列算法 - 滑动窗口问题(常见模型:找出滑动窗口中的最大值/最小值)

欢迎观看我的博客&#xff0c;如有问题交流&#xff0c;欢迎评论区留言&#xff0c;一定尽快回复&#xff01;&#xff08;大家可以去看我的专栏&#xff0c;是所有文章的目录&#xff09; 文章字体风格&#xff1a; 红色文字表示&#xff1a;重难点✔ 蓝色文字表示&#xff1a…

ASEMI肖特基二极管MBR10100FCT图片,MBR10100FCT大小

编辑-Z ASEMI肖特基二极管MBR10100FCT参数&#xff1a; 型号&#xff1a;MBR10100FCT 最大重复峰值反向电压&#xff08;VRRM&#xff09;&#xff1a;100V 最大RMS电桥输入电压&#xff08;VRMS&#xff09;&#xff1a;70V 最大直流阻断电压&#xff08;VDC&#xff09;…

SpringBoot整合Druid数据源

SpringBoot整合Druid数据源1.使用Druid数据源2.完整的Druid配置3.为 DruidDataSource 绑定全局配置文件中的参数4.配置Druid数据源监控5.配置 filter 过滤器1.使用Druid数据源 <!-- https://mvnrepository.com/artifact/com.alibaba/druid --> <dependency><gr…

方差分析 / 主成分分析 / 因子分析 / 聚类分析

一.方差分析 水平&#xff1a;因素的不同状态&#xff0c;分组是按照因素的不同水平划分的 因变量&#xff1a;在分组试验中&#xff0c;对试验对象所观测记录的变量&#xff0c;它是受各因素影响的变量 常见的方差分析类型&#xff1a;单因素方差分析&#xff0c;多因素方差…

RK3568平台开发系列讲解(调试篇)如何跟踪系统事件

🚀返回专栏总目录 文章目录 一、ltrace二、strace三、ptrace沉淀、分享、成长,让自己和他人都能有所收获!😄 📢本文我们要介绍 Linux 上两个非常有用的工具:ltrace 和 strace。在分析软件的运行过程、调试疑难 Bug、执行性能分析和调优等方面,它们都是非常有用的辅助…

肠道核心菌属——优/真杆菌属(Eubacterium),你为什么要关心它?

谷禾健康 Eubacterium 通常翻译为真杆菌属或优杆菌属 Eubacterium&#xff0c;革兰氏阳性细菌&#xff0c;属于真杆菌科&#xff0c;厚壁菌门。 Eubacterium 是在健康人结肠中发现的一种重要的肠道细菌&#xff0c;它是人类肠道微生物群的核心菌属之一&#xff0c;并显示…

SPI通信协议详解

一&#xff0c;SPI的简介 SPI&#xff0c;是英语Serial Peripheral interface的缩写&#xff0c;顾名思义就是串行外围设备接口。是Motorola首先在其MC68HCXX系列处理器上定义的。SPI接口主要应用在 EEPROM&#xff0c;FLASH&#xff0c;实时时钟&#xff0c;AD转换器&#xff…

【力扣刷题】预测赢家

&#x1f517; 题目链接 题目描述 给你一个整数数组 nums 。玩家 1 和玩家 2 基于这个数组设计了一个游戏。 玩家 1 和玩家 2 轮流进行自己的回合&#xff0c;玩家 1 先手。开始时&#xff0c;两个玩家的初始分值都是 0 。每一回合&#xff0c;玩家从数组的任意一端取一个数…

leetcode热题HOT100汇总——持续更新中

文章目录前言1. 两数之和2. 两数相加3.无重复字符的最长子串4. 寻找两个正序数组的中位数5. 最长回文子串10. 正则表达式匹配11. 盛最多水的容器15. 三数之和17. 电话号码的字母组合19. 删除链表的倒数第 N 个结点20. 有效的括号21. 合并两个有序链表前言 为了学习本文捋顺lee…

再多学一点Spring之过滤器与拦截器的区别

学习和使用Spring这么久了&#xff0c;我们都用过过滤器和拦截器&#xff0c;但是真要被问到过滤器和拦截器有什么区别&#xff0c;我又一脸懵逼了&#xff0c;回答不上来。今天我们就来学习一下&#xff0c;过滤器和拦截器。 过滤器 通过实现javax.servlet.Filter接口来自定义…

网站被篡改 收录一些非本网站快照跳转如何解决

在实际的网站运营维护过程中&#xff0c;经常发生网站被HACK攻击等情况&#xff0c;尤其网站的标题被篡改为中文关键词<title>&#xff0c;使得网站在百度搜索的索引结果非常的明显&#xff0c;直接在浏览器里打开网站&#xff0c;用肉眼看到的是未被篡改的首页标题。根据…

计算机学徒等级划分

目录 第一级&#xff1a;菜鸟级 第二级&#xff1a;新手级 第三级&#xff1a;入门级 第四级&#xff1a;精通级 第五级&#xff1a;巅峰级 第一级&#xff1a;菜鸟级 概述&#xff1a;你问他计算机是干什么的&#xff1f;他说&#xff1a;“打游戏的&#xff0c;看电影的…

地下水监测系统的构成,优势是什么?

平升电子地下水监测系统由地下水自动监测站监测设备和监测中心平台软件组成。监测设备自动采集、存储地下水水位、水温、水量、水质数据&#xff0c;通过4G/NB-IoT/北斗无线通信网络定时上报至省/市/县级监测中心平台&#xff0c;平台自动接收和存储数据&#xff0c;并对地下水…

排查 Edge WebView2 在某个设备上不出图像的问题

我们在 Windows 应用内嵌入 Edge WebView2&#xff0c;来展示部分用网页实现的界面。总得来说还是不错的&#xff0c;比如&#xff1a; 渲染很快&#xff0c;基本上内置网页100毫秒以内控件样式很清爽&#xff0c;没有多余界面开发需要调用的 API 也不多 但是最近在某个用户那…

2022年山东最新建筑施工架子工(建筑特种作业)模拟题库及答案

百分百题库提供特种工&#xff08;架子工&#xff09;考试试题、特种工&#xff08;架子工&#xff09;考试预测题、特种工&#xff08;架子工&#xff09;考试真题、特种工&#xff08;架子工&#xff09;证考试题库等,提供在线做题刷题&#xff0c;在线模拟考试&#xff0c;助…

21天学Python --- 打卡12:python执行jar包

21天学Python --- 打卡12:Python执行jar包1. Subprocess1.1 Method1.2 Execure Jar2. Jpype2.1 Install2.2 Execute Simple Jar2.2.1 Java Code2.2.2 Python Code2.3 Execute Maven Jar3. Awakening1. Subprocess subprocess 是 Python 2.4 中新增的一个模块 1.1 Method subpro…

第二证券|医药板块短期轮动加快 机构看好后市机会

作为现在商场行情继续性最强的职业板块&#xff0c;医药股昨日早盘呈现回调&#xff0c;午后继续反弹&#xff0c;体现出较强的承接动能。机构以为&#xff0c;本轮新冠医治概念主线行情或仍将延续&#xff0c;但个股或将继续分解。 在本轮反弹行情中&#xff0c;医药板块表现较…