【C++修炼之路】模板初阶

news2024/11/24 7:26:28

👑作者主页:@安 度 因
🏠学习社区:StackFrame
📖专栏链接:C++修炼之路

文章目录

  • 一、泛型编程
  • 二、函数模板
    • 1、概念
    • 2、格式
    • 3、函数模板实例化
      • ① 隐式
      • ② 显式
    • 4、特性
  • 五、类模板
  • 六、模板使内置类型"升级"

如果无聊的话,就来逛逛 我的博客栈 吧! 🌹

一、泛型编程

C 语言为什么不提供数据结构?因为不支持泛型编程。对于一个栈,它可能只支持存取 int 类型,它并不支持 泛型 – 广泛类型 的存取。

看一个交换函数:

void Swap(int& x1, int& x2)
{
	int tmp = x1;
	x1 = x2;
	x2 = tmp;
}

void Swap(double& x1, double& x2)
{
	double tmp = x1;
	x1 = x2;
	x2 = tmp;
}

int main()
{
	int a = 0, b = 1;
	double c = 1.1, d = 2.2;

	return 0;
}

C++ 对于不同类型的数据交换,可以使用函数重载,实现不同的函数,来交换。

但是每增加一个类型的交换,就得再写一个 Swap ,是不是很麻烦?而且若一个重载函数写错,其他可能也都会写错。那这样好吗?并不好。

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

生活中,我们浇筑的时候,可以使用不同的颜色填充浇筑目标:

image-20230208115038618

而这些交换函数的区别就是类型,类型就好比是颜色,而函数就是模具

如果在C++中,也能够存在这样一个模具,通过给这个模具中填充不同材料(类型),来获得不同材料的铸件(即生成具体类型的代码),那将会节省许多头发 。巧的是前人早已将树栽好,我们只需在此乘凉。

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

而模板,分为两种:

image-20230208115222894

二、函数模板

1、概念

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

2、格式

template<typename T1, typename T2,…,typename Tn> or template

它们有区别,但是在我们接下来的举例中没有区别,其他点之后我们再看。

改造:

在这里插入图片描述

(一般叫 T – typename )

3、函数模板实例化

这三个地方调用的是同一个函数吗?不是,因为它们调用函数的类型不一样,指令也就不一样。

它只是一个模具,供别人使用,它并不是说共用一份函数,而是类型可以通过这个模板,来进行不同类型的函数调用。

对于一个函数模板只能对于一个函数,对于下一个函数,则不支持该模板。

① 隐式

这一过程就叫模板实例化

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

image-20230208121123391

实际调用的是实例化后的函数

汇编验证:

image-20230208121514801

image-20230208121435484

本质没变,但是我们省劲了(只管调用,不管生成)。

② 显式

但是有时候由于类型的原因,隐式不能推导成功:

image-20230208122150234

a1 是 int ,d1 是 double ,矛盾了。

这时可以强转:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-03p7lU3j-1689668344405)(https://anduin.oss-cn-nanjing.aliyuncs.com/image-20230208122238842.png)]

但是有些取巧,所以我们一般使用 显式实例化

image-20230208122342187

这种方式,会根据 <> 中的类型,进行实例化。调用时会将参数隐式类型转换为实例化后的参数类型。

4、特性

1)模板参数能写多个

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

int main()
{
	int a1 = 10, a2 = 20;
	double d1 = 10.0, d2 = 20.0;
	Add(a1, d1);
	return 0;
}

但是返回值的类型就起了决定性的作用:

比如 Add(a1, d1) 此刻的 T1 是 int ,T2 是 double ,计算时,结果为 double ,但是返回时,返回的是 T1 ,所以又会进行隐式类型转换。需要考虑清楚。

image-20230208145450660

2)函数模板和普通函数可以同时出现,普通函数优先级高

image-20230208145640747

因为普通函数可以直接调用;但是函数模板需要推演实例化,再调用。

五、类模板

对于 C 来说,可以通过 typedef 来改变栈中元素类型:

typedef int DataType;
typedef double DataType;

class Stack
{
public:
	Stack(int capacity = 4)
		:_top(0)
		, _capacity(capacity)
	{
		_a = new int[capacity];
	}

	~Stack()
	{
		delete[] _a;
		_a = nullptr;
		_capacity = _top = 0;
	}

private:
	DataType* _a;
	int _top;
	int _capacity;
};

int main()
{
	return 0;
}

但是对于这种情况:

Stack st1; // int 类型
Stack st2; // double 类型

这样是不行的,就算 typedef 也不管用。不能同时存储两个类型 。对于这种方式,只能写两个栈,一个存 int ,一个存 double :

class Stackint
{
public:
	// ...
private:
	int* _a;
	// ...
};

class Stackdouble
{
public:
    // ...
private:
    double* _a;
    // ...
};

int main()
{
    Stackint st1; // int
    Stackdouble st2; // double
}

这样子就很繁琐且冗余,但是没有办法,C 并不存在模板。C++ 为了解决这个问题,就诞生了 类模板

template<class T> // 类模板
class Stack 
{
public:
	Stack(int capacity = 4)
		:_top(0)
		, _capacity(capacity)
	{
		_a = new T[capacity];
	}

	~Stack()
	{
		delete[] _a;
		_a = nullptr;
		_capacity = _top = 0;
	}

private:
	T* _a;
	int _top;
	int _capacity;
};

int main()
{
	Stack<int> st1; // int
	Stack<double> st2; // double

	return 0;
}

无法推演,因为是创建对象,而并不是函数调用,所以需要显式实例化指定:Stack<int> st1Stack 是类名,Stack 是类型

声明和定义分离

template<class T>
class Stack
{
    // ...
};

template<class T> 
void Stack<T>::Push(const T& x) // Stack<T> 指定
{}

int main()
{
    Stack<int> st1;
    st1.Push(1);
}

当声明和定义分离时,一个模板只能给一个函数用,但是对于类中,一个模板参数对于整个类都能用。

没写模板:

image-20230522125623271

写模板:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KUhKA8ON-1689668344406)(https://anduin.oss-cn-nanjing.aliyuncs.com/image-20230522125554519.png)]

六、模板使内置类型"升级"

当有了模板之后,对于内置类型进行了升级,内置类型也具有了默认构造函数。

vector(size_t n, const T& val = T())

对于内置类型,为了与自定义类型的行为保持一致,使用默认构造函数是必要的。尽管内置类型没有显式的构造函数,但在使用模板时,为了满足容器类型的要求,需要提供一个通用的初始化方式。

使用 T() 作为默认构造函数的默认参数,可以确保在使用容器类型时,无论是自定义类型还是内置类型,都能够正常进行对象的初始化。

这里显示的构造函数泛指 —— 没有代码编写的构造函数:

内置类型没有像自定义类那样通过代码编写的构造函数。在C++中,自定义类可以定义自己的构造函数来控制对象的初始化过程,但内置类型没有这种能力。

内置类型的初始化是由编译器隐式处理的。编译器在声明内置类型的变量时,会自动执行适当的初始化。例如,整数类型会被初始化为0,浮点数类型会被初始化为0.0,布尔类型会被初始化为false这种形式。

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

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

相关文章

无人驾驶中识别颜色并跟踪的优化(加入PID算法控制)

我们了解到无人驾驶是如何去识别颜色的&#xff0c;以及无人车能够跟随颜色目标的演示。回到现实中我们发现&#xff0c;无人车的速度控制是很关键的&#xff0c;这个涉及到安全问题&#xff0c;比如等待红绿灯时&#xff0c;该减速或加速超车等这样很常见的情形&#xff0c;在…

论文笔记--OpenPrompt: An Open-source Framework for Prompt-learning

论文笔记--OpenPrompt: An Open-source Framework for Prompt-learning 1. 文章简介2. 文章概括3 文章重点技术4. 文章亮点5. 原文传送门 1. 文章简介 标题&#xff1a;OpenPrompt: An Open-source Framework for Prompt-learning作者&#xff1a;Ning Ding, Shengding Hu, We…

基于Java+SpringBoot+vue前后端分离在线视频教育平台设计实现

博主介绍&#xff1a;✌全网粉丝30W,csdn特邀作者、博客专家、CSDN新星计划导师、Java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专…

Leecode316: 去除重复字母

下面这里使用有序map——TreeMap来实现Map接口&#xff0c;但是相对顺序是不能改变的&#xff01;这样会使得后面的跑到前面去&#xff0c;所以有问题 最简单的思想肯定是暴力思想&#xff0c;就是从前往后寻找&#xff0c;一旦遇到存在的情况就. 重点在于明确两点&#xff1a…

HarmonyOS学习路之方舟开发框架—学习ArkTS语言(基本语法 五)

Styles装饰器&#xff1a;定义组件重用样式 如果每个组件的样式都需要单独设置&#xff0c;在开发过程中会出现大量代码在进行重复样式设置&#xff0c;虽然可以复制粘贴&#xff0c;但为了代码简洁性和后续方便维护&#xff0c;我们推出了可以提炼公共样式进行复用的装饰器St…

【LLM】self-instruct 构建指令微调数据集

文章目录 一、self-instruct流程二、具体过程1. 指令生成2. 分类任务识别3. 实例生成4. 过滤和后处理 三、其他部分1. 验证数据质量2. GPT3SELF-INSTRUCT生成数据的词性分析3. Rouge-L指标 Reference 一、self-instruct流程 四部曲&#xff1a;指令生成&#xff1b;分类任务识别…

Oracle 的视图

Oracle 的视图 源数据&#xff1a; -- Create table create table STU_INFO (id NUMBER not null,name VARCHAR2(8),score NUMBER(4,1),class VARCHAR2(2) ) tablespace STUDENTpctfree 10initrans 1maxtrans 255storage(initial 64Knext 1Mminextents 1maxextents unlim…

最多变的混合模式-实色混合HardMix

最多变的混合模式-实色混合HardMix 之前写过一篇介绍27种图层混合模式的非常详细&#xff0c;如果你想完全了解底层的原理&#xff0c;这篇文章不会让你失望。 PS图层混合模式超详细解答-图层混合模式的原理 - 王先生的副业的文章 - 知乎 https://zhuanlan.zhihu.com/p/64396…

从小白到大神之路之学习运维第63天--------zabbix企业级监控(概述、单台服务器监控本身安装部署)

第三阶段基础 时 间&#xff1a;2023年7月18日 参加人&#xff1a;全班人员 内 容&#xff1a; zabbix企业级监控 目录 一、Zabbix概述 &#xff08;一&#xff09;Zabbix简介 &#xff08;二&#xff09;Zabbix运行条件&#xff1a; &#xff08;三&#xff09;Zab…

深入解析 YAML 配置文件:从语法到最佳实践

一、认识YAML YAML&#xff08;YAML Aint Markup Language&#xff09;是一种人类可读的数据序列化语言。它的设计目标是使数据在不同编程语言之间交换和共享变得简单。YAML采用了一种简洁、直观的语法&#xff0c;以易于阅读和编写的方式表示数据结构。YAML广泛应用于配置文件…

探索开源图片编辑工具:定制化编辑,激发想象

图片编辑是现代生活中广泛使用的技术&#xff0c;它不仅能够改善照片和图像的质量&#xff0c;还能创造出令人赞叹的视觉效果。随着开源文化的兴起&#xff0c;越来越多的开源工具涌现出来&#xff0c;为我们提供了实用且灵活的图片编辑功能。这些开源工具的出现为个人、设计师…

想用vivo手机设置一个5天后提醒我的闹铃,怎么设置?

在生活和工作中有很多待办事项&#xff0c;都不是需要当前立刻就去完成的&#xff0c;而且需要我们提前记住&#xff0c;并且在未来的某个指定日期去完成&#xff0c;例如两天后提交项目报告、下周五的重要会议、考试报名时间等。如果担心自己忘记这些待办事项&#xff0c;应该…

android APP外包开发的三种方式

开发android APP有三种方式&#xff0c;分别是原生开发、混合开发和无代码开发&#xff0c;原生开发对开发者有一定要求&#xff0c;但用户体验好&#xff1b;混合开发是使用H5开发&#xff0c;对开发者要求相对较低&#xff1b;而无代码开发则是通过操作界面搭建APP&#xff0…

openGauss学习笔记-13 openGauss 简单数据管理-DELETE语句

文章目录 openGauss学习笔记-13 openGauss 简单数据管理-DELETE语句13.1 语法格式13.2 参数说明13.3 示例 openGauss学习笔记-13 openGauss 简单数据管理-DELETE语句 DELETE语句可以从指定的表里删除满足WHERE子句的行。如果WHERE子句不存在&#xff0c;将删除表中所有行&…

传输层协议—网络

文章目录 1.TCP1.1TCP协议段格式1.2可靠机制1.2.1确认应答机制1.2.2超时重传机制1.2.3连接管理机制1.2.4流量控制机制1.2.5拥塞控制机制 1.3效率机制1.3.1滑动窗口机制1.3.2延迟应答机制1.3.3捎带应答机制 1.4粘包问题&#xff08;tcp问题&#xff0c;应用层的数据包&#xff0…

JMeter 性能测试实例分析

一、性能测试分类&#xff1a; 1、基准测试 2、并发测试 3、负载测试 4、压力测试 1、基准测试&#xff1a; 也是单用户测试&#xff0c;测试环境确定以后&#xff0c;对业务模型中的重要业务做单独的测试&#xff0c;获取单用户运行时的各项性能指标&#xff0c;为多用户…

C# 细说async/await的用法

目录 一&#xff0c;引言 二&#xff0c;实例演示 2.1 多线程同步执行下载任务&#xff0c;任务完成后通知 2.2 异步执行下载任务&#xff0c;任务完成后通知 三&#xff0c;async/await的用法 3.1 跨线程修改UI控件 3.2 异步获取数据 一&#xff0c;引言 首先先来区分…

【Go】实现一个代理Kerberos环境部分组件控制台的Web服务

实现一个代理Kerberos环境部分组件控制台的Web服务 背景安全措施引入的问题SSO单点登录 过程整体设计路由反向代理登录会话组件代理YarnHbase 结果 背景 首先要说明下我们目前有部分集群的环境使用的是HDP-3.1.5.0的大数据集群&#xff0c;除了集成了一些自定义的服务以外&…

python opencv 级联Haar多目标检测

一、基于OpenCV的haar分类器实现笑脸检测 1、Haar分类器介绍 &#x1f680;Haar分类器是一种基于机器学习的目标检测算法&#xff0c;它使用Haar特征描述图像中的目标。Haar特征是基于图像亮度的局部差异计算得出的&#xff0c;可以用来描述目标的边缘、角落和线条等特征。 使用…

Jenkins (一)

Jenkins (一) Docker Jenkins 部署 一. 安装 jenkins $ mkdir -p /home/tester/data/docker/jenkins $ vim jenkins:lts-jdk11.sh./jenkins:lts-jdk11.sh 内容 #! /bin/bash mkdir -p /home/tester/data/docker/jenkins/jenkins_homesudo chown -R 1000:1000 /home/tester/da…