C++:模版初阶

 

目录

1. 泛型编程

2.函数模版 

2.1. 函数模版的用法 

2.2. 函数模版的原理 

2.3 函数模板的实例化 

2.4 模版参数的匹配原则 

3. 类模版 

3.1 类模版的格式 

3.2. 类模版的实例化 


1. 泛型编程

如何实现整形、字符串,或者其他自定义类型的交换函数? 

 

我们可以利用C++中函数重载实现。 

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

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

为了解决这个问题,C++就引入了模版这个概念,并且依次延伸出了泛型编程的思想。**泛型编程:**编写与类型无关的通用代码,是代码复用的一种手段。模板是泛型编程的基础。 

模版简单来说就是像一个道具模子,根据不同的需求,可以产生不同的形态。而模版即可以分为函数模版,又可以分为类模版。 

2.函数模版 

2.1. 函数模版的用法 

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

  1. template<typename T1, typename T2,......,typename Tn>
  2. 返回值类型 函数名(参数列表){}

然后我们就可以用模版定义一个针对不同类型的交换函数: 

#include<iostream>
using namespace std;
template<typename T>
void Swap(T& x, T& y)
{
	T tmp = x;
	x = y;
	y = tmp;
}
int main()
{
	int a = 1;
	int b = 2;
	double n = 1.1;
	double m = 2.2;
	cout << "交换前:" << a << "," << b << endl << endl;
	cout << "交换前:" << n << "," << m << endl << endl;
	Swap(a, b);
	Swap(n, m);
	cout << "交换后:" << a << "," << b << endl;
	cout << "交换后:" << n << "," << m << endl;
}

 

  • 注意:typename是用来定义模板参数关键字,也可以使用class
2.2. 函数模版的原理 

学会了如何使用函数模版之后,我们就可能会有一个疑问:那就是不同的类型调用的函数模版是同一个函数吗?这时我们继续利用上面代码,通过调用汇编来观察一下: 

 

函数的指令并不一样,这说明调用的根本不是同一个函数!!那原理究竟是怎样的呢?

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

 

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

2.3 函数模板的实例化 

 2.3.1. 隐式实例化

template<class T>
T Add(const T& left, const T& right)
{
	return left + right;
}
int main()
{
	//编译器自动推导类型
	cout << (Add(3, 5)) << endl;
	return 0;
}

2.3.2. 显示实例化 

但是有些场景下就不能利用隐式实例化的方式调用函数模版,比如说: 

template<class T>
T Add(const T& left, const T& right)
{
	return left + right;
}
int main()
{
	int a = 1;
	double b = 1.1;
	cout << (Add(a, b)) << endl;
	return 0;
}

 

这时编译器就会报错,因为编译器不知道该将T推演成int还是double。为了解决这个问题,就需要让编译器提前知道该将T推演成什么类型,常见有两种解决方法: 

一种方法就是先强制类型转换: 

template<class T>
T Add(const T& left, const T& right)
{
	return left + right;
}
int main()
{
	int a = 1;
	double b = 1.1;
	cout << (Add(a, (int)b)) << endl;
	return 0;
}

 

还要一种方式就是显示实例化:在函数名后的<>中指定模板参数的实际类型。 

template<class T>
T Add(const T& left, const T& right)
{
	return left + right;
}
int main()
{
	int a = 1;
	double b = 1.1;
	cout << (Add<int>(a, b)) << endl;//指定T实例化为int型
	return 0;
}

  int转double还好点,double转int会造成精度丢失 

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

除了以上场景需要显式实例化外,还有一种场景也需要我们显式实例化: 

2.4 模版参数的匹配原则 
  1. 一个非模板函数可以和一个同名的函数模板同时存在,而且该函数模板还可以被实例化为这个非模板函数。
// 专门处理int的加法函数
int Add(int left, int right)
{
	cout << "int Add(int left, int right)" << endl;
	return left + right;
}
// 通用加法函数
template<class T>
T Add(T left, T right)
{
	cout << "T Add(T left, T right)" << endl;
	return left + right;
}
void Test()
{
	Add(1, 2); // 与非模板函数匹配,编译器不需要特化
	Add<int>(1, 2); // 调用编译器特化的Add版本
}

 

 

如果有普通函数可以匹配,那么就不会去调用模版函数,但如果我们非得使用模版的Add函数,只需要进行显示实例化即可! 

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

// 专门处理int的加法函数
int Add(int left, int right)
{
	cout << "int Add(int left, int right)" << endl;
	return left + right;
}
// 通用加法函数
template<class T1, class T2>
T1 Add(T1 left, T2 right)
{
	cout << "T1 Add(T1 left, T2 right)" << endl;
	return left + right;
}
void Test()
{
	Add(1, 2); // 与非函数模板类型完全匹配,不需要函数模板实例化
	Add(1, 2.0); // 模板函数可以生成更加匹配的版本,编译器根据实参生成更加匹配的Add函
}

 

3. 类模版 

类模版与函数模版类似,只不过作用对象为类。 

3.1 类模版的格式 

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

template<class T>
class AA
{
public:
	AA(T a1, T a2)
		:_a1(a1),_a2(a2)
	{
		;
	}
	T Add();
private:
	T _a1;
	T _a2;
};
  • 注意:类模板中函数放在类外进行定义时,需要加模板参数列表

模版的声明和定义不能放在不同的文件里,应该统一放在头文件里最合适。 

 

3.2. 类模版的实例化 

类模板实例化与函数模板实例化不同,类模板实例化需要在类模板名字后跟<>,然后将实例化的类型放在<>中即可,类模板名字不是真正的类,而实例化的结果才是真正的类。所以利用类模版创建对象时必须要对类模版先显式实例化。 

void Test()
{
	//创建对象时必须要先实例化
	AA<int> p1(1,2);
	AA<double> p2(1.1,2.2);
}

注意:AA类名AA<double>才是类型 

 

 

 

 

 

 

 

 

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

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

相关文章

028.爬虫专用浏览器-抓取#shadowRoot(closed)下的内容

一、什么是Shadow DOM Shadow DOM是一种在web开发中用于封装HTML标记、样式和行为的技术&#xff0c;以避免组件间的样式和脚本冲突。它允许开发者将网页的一部分隐藏在一个独立的作用域内&#xff0c;从而实现更加模块化和可维护的代码结构 二、js操作Shadow DOM // 获取宿…

命名空间std, using namespace std

命名空间std&#xff0c;using namespace std 在标准C以前&#xff0c;都是用#include<iostream.h>这样的写法的&#xff0c;因为要包含进来的头文件名就是iostream.h。标准C引入了名字空间的概念&#xff0c;并把iostream等标准库中的东东封装到了std名字空间中&#x…

React类组件详解

React类组件是通过创建class继承React.Component来创建的&#xff0c;是React中用于构建用户界面的重要部分。以下是对React类组件的详细解释&#xff1a; 一、定义与基本结构 类组件使用ES6的class语法定义&#xff0c;并继承自React.Component。它们具有更复杂的功能&#xf…

笔记整理—linux驱动开发部分(2)模块信息与编译

对于linux而言&#xff0c;.ko文件为驱动文件&#xff0c;在终端可以使用lsmod列出已经安装的模块&#xff0c;使用insmod xxx.ko安装所需要的模块&#xff0c;modinfo xxx.ko打印某个模块提供的信息&#xff0c;rmmod xxx卸载某个不需要的模块。 insmod与module_init宏。在源代…

智能台灯设计(一)原理图设计

1. 前言 作者最近突发奇想&#xff0c;想自己做一个小台灯&#xff0c;设想的功能有&#xff1a;带锂电池可充电、可以调节亮度&#xff0c;后续通过增加WIFI模块实现手机控制开关功能。目前先实现最简单的功能&#xff0c;有时间再一步步完善吧。 2. 原理图设计 充电芯片使用…

[LitCTF 2023]破损的图片(初级)的write up

开启靶场&#xff0c;下载附件&#xff0c;解压后得到文件&#xff1a; 破损的图片&#xff0c;那应该是文件头缺失 用010editor打开&#xff1a; 文件尾是AE 42 60 82&#xff0c;说明原图片是png格式&#xff0c;文件头前八个字节是3F&#xff0c;且对应8个"?"&am…

0softmax和背后的最大熵(极大似然法)

只要无穷阶矩都一样&#xff0c;那么两个分布一定一样。 整理思路&#xff1a;1、设定样本的概率模型与目标概率模型一致&#xff08;两个模型特性函数一致&#xff09;建立服从伯努利分布的变量&#xff08;此处需要理解样本空间及变量的关系&#xff09;对两个模型进行降维&a…

Could not retrieve mirrorlist http://mirrorlist.centos.org错误解决方法

文章目录 背景解决方法 背景 今天在一台新服务器上安装nginx&#xff0c;在这个过程中需要安装相关依赖&#xff0c;在使用yum install命令时&#xff0c;发生了以下报错内容&#xff1a; Could not retrieve mirrorlist http://mirrorlist.centos.org/?release7&archx8…

HarmonyOS 相对布局(RelativeContainer)

1. HarmonyOS 相对布局&#xff08;RelativeContainer&#xff09; 文档中心:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/arkts-layout-development-relative-layout-V5   RelativeContainer为采用相对布局的容器&#xff0c;支持容器内部的子元素设…

ssh服务器相关实验

相关命令 下载软件 yum install openssh-server 查看公私钥 ll /etc/ssh/ 查看配置文件 rpm -qf /etc/ssh/sshd_config 修改ssh端口号&#xff08;改成2222&#xff09; vim /etc/ssh/sshd_config 拒绝root用户远程登录 进入配置文件所在文件夹 cd /etc/ssh/sshd_config.d/ 进…

ionic Capacitor 生成 Android 应用

官方文档 https://ionic.nodejs.cn/developing/android/ https://capacitorjs.com/docs/getting-started 1、创建新的 Capacitor 应用程序 空目录下面 npm init capacitor/app2、install Capacitor npm install npm start在这里插入图片描述 3、生成dist目录 npm run buil…

【ArcGIS Pro实操第5期】全局及局部空间插值:GPI、LPI、IDW等

ArcGIS Pro实操第5期&#xff1a;全局及局部空间插值 ArcGIS Pro-用于空间插值的丰富工具箱实操&#xff1a;空间插值方法1&#xff1a;Trend Surface Model for Interpolation-以降水数据为例方法2&#xff1a;Kernel Density Estimation Method-以单位面积鹿的目击数为例方法…

用接地气的例子趣谈 WWDC 24 全新的 Swift Testing 入门(一)

概述 从 WWDC 24 开始&#xff0c;苹果推出了全新的测试机制&#xff1a;Swift Testing。利用它我们可以大幅度简化之前“老态龙钟”的 XCTest 编码范式&#xff0c;并且使得单元测试更加灵动自由&#xff0c;更符合 Swift 语言的优雅品味。 在这里我们会和大家一起初涉并领略…

基于SSM美容院管理系统的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;用户管理&#xff0c;套餐类型管理&#xff0c;美容预约管理&#xff0c;生日提醒管理&#xff0c;管理员管理&#xff0c;系统管理 员工账号功能包括&#xff1a;系统首页&#xff0c;个人中心&#…

普推知产:申请商标名称从4字改成3字下了初审!

近日7月的时候普推知产老杨帮客户申请的水果猕猴桃31类商标&#xff0c;初步审定公告下来了&#xff0c;基本没什么问题三个月公告结束后一个月内就可以拿到商标注册证&#xff0c;客户所在地全国有名猕猴桃之县&#xff0c;同质化竞争还得需要商标才可以。 刚开始了解到这位做…

Three.js遮罩多场景穿梭过渡

仓库 思路&#xff1a; 渲染一个遮罩 亮的区域为需要显示另一个场景的区域 在靠近门时完全渲染一个场景 在穿过门的同时切换场景关系 if (this.Doors.length) {// 材质变为黑色 除了“门”toggleRoughnessMaterial("black");// 设置RenderTarget保存结果renderer.se…

【Linux系统】为什么环境变量具有全局性?共享?写时拷贝优化?

环境变量表具有全局性的原因&#xff1a; 环境变量表之所以具有全局性的特征&#xff0c;主要是因为它们是在进程上下文中维护的&#xff0c;并且在大多数操作系统中&#xff0c;当一个进程创建另一个进程&#xff08;即父进程创建子进程&#xff09;时&#xff0c;子进程会继承…

网站建设前需要搞清楚哪些问题

网站建设前需要搞清楚的问题涉及多个方面&#xff0c;以下是一些关键问题的概述&#xff1a; 明确目标和目的 企业宣传与品牌塑造&#xff1a;网站是企业展示形象、传播品牌的重要窗口。通过精心设计的网站界面和内容布局&#xff0c;可以向潜在客户传递企业的价值观、文化理念…

iOS AVAudioSession 详解【音乐播放器的配置】

前言 在 iOS 音频开发中&#xff0c;AVAudioSession 是至关重要的工具&#xff0c;它控制着应用的音频行为&#xff0c;包括播放、录音、后台支持和音频中断处理等。对于音乐播放器等音频需求强烈的应用&#xff0c;设计一个合理的 AVAudioSession 管理体系不仅能保证音频播放…

[JAVAEE] 多线程的案例(三) - 线程池

目录 一. 什么是线程池 二. 线程池的作用 三. java提供的线程池类 四. ThreadPoolExecutor的构造方法及参数理解 1. int corePoolSize: 核心线程数. 2. int maximumPoolSize: 最大线程数 核心线程数 非核心线程数 3. int keepAliveTime:非核心线程允许空闲的最大时间. …