C++之模版初阶

news2025/1/12 6:00:09

目录

前言

1.泛型编程

2.函数模版

2.1函数模版概念

2.2函数模版格式

2.3函数模版的原理

2.4函数模版的实例化

 2.5模版参数的匹配原则

 3.类模版

3.1类模版的定义格式

3.2类模版的实例化

结束语


前言

前面我们学习了C++的类与对象和内存管理,接下来我们继续学习C++的相关知识!!!

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函数模版概念

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

2.2函数模版格式

template<typename T1,typename T2,...typename Tn>

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

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

例如实现交换函数

template<typename T>
void Swap(T& x, T& y) {
	T temp = x;
	x = y;
	y=temp;
}
int main() {
	double a = 3.4, b = 4.3;
	Swap(a, b);
	cout << a << " " << b << endl;
	char x = a, y = b;
	Swap(x, y);
	cout << x << " " << y << endl;
	return 0;
}

2.3函数模版的原理

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

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

2.4函数模版的实例化

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

1.隐式实例化

隐式实例化是指编译器在使用模板的函数时,根据传入的参数类型自动推导出模板参数,并生成相应的函数实例。这种方式通常在编写代码时不需要显式指定类型,编译器会根据上下文进行推导。 

template<class T>
T Add(const T& left, const T& right)
{
	return left + right;
}
int main()
{
	int a1 = 10, a2 = 20;
	double d1 = 10.1, d2 = 20.0;
	cout<<Add(a1, a2)<<endl;
	cout<<Add(d1, d2)<<endl;
}

如果计算a1+d1,就会出问题了,即Add(a1, d1);

/* 该语句不能通过编译,因为在编译期间,当编译器看到该实例化时,需要推演其实参类型 通过实参a1将T推演为int,通过实参d1将T推演为double类型,但模板参数列表中只有 一个T, 编译器无法确定此处到底该将T确定为int 或者 double类型而报错 */

此时有两种处理方式:1. 用户自己来强制转化 2. 使用显式实例化 3.定义多个类

Add(a, (int)d); 

Add<int>(a1, d1)

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

2.显式实例化

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

int main(void)
 {
 int a = 10;
 double b = 20.1;
 // 显式实例化
Add<int>(a, b);
 return 0;
 }

 2.5模版参数的匹配原则

1. 一个非模板函数可以和一个同名的函数模板同时存在,而且该函数模板还可以被实例化为这 个非模板函数。

int Add(int x, int y) {
	return x + y;
}
template<class T1,class T2>
T2 Add(const T1& left, const T2& right)
{
	return left + right;
}
int main()
{
	int a1 = 10, a2 = 20;
	double d1 = 10.1, d2 = 20.0;
	cout<<Add(a1, a2)<<endl; 
// 与非模板函数匹配,编译器不需要特化
	cout << Add<int>(a1, a2) << endl;
//调用编译器特化的Add版本
}

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

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

3. 模板函数不允许自动类型转换,但普通函数可以进行自动类型转换。

int Add(int x, int y) {
	return x + y;
}
/*
template<class T1,class T2>
T2 Add(const T1& left, const T2& right)
{
	return left + right;
}
*/
int main()
{
	int a1 = 10, a2 = 20;
	double d1 = 10.1, d2 = 20.0;
	//cout<<Add(a1, a2)<<endl;
	//cout << Add<int>(a1, a2) << endl;
	cout << Add(a1, d1) << endl;
	//cout<<Add(d1, d2)<<endl;
}

 3.类模版

3.1类模版的定义格式

template<class T1,classT2,...class Tn>

class 类模板名

{ // 类内成员定义 };  

下面我们实现一个栈的类模版

#include <iostream>
#include <cassert>
using namespace std;
template<class T>
class Stack {
public:
	Stack(size_t n = 4) 
		:_arr(new T[n]),
		_size(0),
		_capacity(n)
	{}
	~Stack() {
		delete[] _arr;
		_arr = nullptr;
		_size = _capacity = 0;
	}
	void push(const T& x) {
		if (_size == _capacity) {
			T* temp = new T[_capacity * 2];
			memcpy(temp, _arr, sizeof(T) * _size);
			delete[]_arr;
			_arr = temp;
			_capacity = _capacity * 2;

		}
		_arr[_size++] = x;
	}
	void pop() {
		assert(_arr);
		assert(_size>0);
		_size--;
	}
	T top() {
		assert(_arr);
		assert(_size > 0);
		return _arr[_size - 1];
	}
	bool empty()const {
		return _size == 0;
	}
private:
	T* _arr;
	size_t _size;
	size_t _capacity;
}; 
int main() {
	//类模版都是显式实例化
		Stack <int>s1;
		s1.push(1);
		s1.push(2);
		s1.push(3);
		s1.push(4);
		while (!s1.empty()) {
			cout << s1.top()<<endl;
			s1.pop();
		}
		Stack <double>s2;
		s2.push(1.1);
		s2.push(2.2);
		s2.push(3.3);
		s2.push(4.4);
		while (!s2.empty()) {
			cout << s2.top() << endl ;
			s2.pop();
		}
	return 0;
}

相较于C语言的typedef定义数据类型(只能实现一种数据栈),这里我们可以实现多种数据类型栈。

3.2类模版的实例化

类模板实例化与函数模板实例化不同,类模板实例化需要在类模板名字后跟<>,然后将实例化的 类型放在<>中即可,类模板名字不是真正的类,而实例化的结果才是真正的类。

// Stack是类名,Stack<int>才是类型

Stack <int>s1;

Stack<double>s2;

如果我们想在类外实现Stack的构造等部分引用了类模版的函数,如果只是单纯的函数声明是仅仅不够的,我们还要再次定义一个类模版。

例如:

template<class T>
void Stack<T>:: push(const T& x) {
	if (_size == _capacity) {
		T* temp = new T[_capacity * 2];
		memcpy(temp, _arr, sizeof(T) * _size);
		delete[]_arr;
		_arr = temp;
		_capacity = _capacity * 2;

	}
	_arr[_size++] = x;
}

结束语

本期内容就到此结束,关于模版进阶的内容后续会讲解的,下节我们将对STL进行一些说明,

最后感谢各位友友的支持,点个赞吧!!!

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

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

相关文章

docker资源控制与数据卷

docker数据卷 容器和宿主机之间数据共享---------挂载卷------------容器内的目录和宿主机的目录进行挂载。实现数据文件共享。 容器的生命周期有限&#xff0c;一旦重启所有对容器内部文件数据修改以及保存的数据都会被初始化&#xff0c;为了防止数据的丢失&#xff0c;重要…

uni-app发布安卓app打包时必须在APP模块配置中选中需要的模块

最近尝试开发一个语音对话的app&#xff0c;在调试阶段没有选中“record录音”模块&#xff0c;安卓基座运行在雷电模拟器上是没有问题的&#xff0c;但是直接云打包在手机上运行就不行了&#xff0c;语音输入没有反应。 经过试验发现是manifest.json没有勾选APP模块配置中的“…

在本地电脑部署属于你的AI大模型

前言&#xff1a; 现在的ai很多&#xff0c;让我看得有些眼花缭乱&#xff0c;随着ai的发展&#xff0c;现在已经有很多ai的大模型已经支持开源&#xff0c;所以现在支持部署一个大模型在自己电脑上&#xff0c;数据私人化。 如果不知道自己是否需要或者是否合适配置可以跳到…

Unity(2022.3.38LTS) - 初步学习C#

目录 一. C#简介 二. 配置代码编辑器 三. C#基本语法 四. 创建脚本 创建C#脚本的方式 1. 在文件夹新建 2. 直接在物体组件创建 五. 例子 扩展物体旋转的方式: 一. C#简介 Unity 中使用的 C# 是一种面向对象的编程语言&#xff0c;具有强大的功能和广泛的应用。 特点…

【笔记】MSPM0G3507移植RT-Thread——MSPM0G3507与RT_Thread(二)

一.创建新工程 找到"driverlib\empty"空白工程&#xff0c;CTRLC然后CTRLV复制副本 重命名为G3507_RTT 打开KEIL工程 双击empty.syscfg&#xff0c;然后打开SYSCONFIG 我的不知道为啥没有48pin选项&#xff0c;如果你也一样&#xff0c;可以跟着我做&#xff0c;如果…

WebDeveloper:1靶机

端口扫描 靶机ip地址为192.168.153.158 目录扫描 访问80端口 拼接访问 /ipdata 发现了一个流量包 放在 wireshark 查看&#xff0c;找到 账号密码 账号&#xff1a;webdeveloper 密码&#xff1a;Te5eQg&4sBS!Yr$)wf%(DcAd 拼接 /wp-login.php 找到登录框 登录成功 找…

vue3+Element Plus功能组件封装——顶部导航(动态渲染+样式调整)

网页顶部的导航栏&#xff0c;一般由代码动态生成 菜单数据在文件内统一配置&#xff0c;方便增删改查&#xff0c;导入后可自动生成导航菜单 代码如下 1.dom部分&#xff08;简单示例&#xff09; <el-menu mode"horizontal" :default-active"currentPath…

java Spring|day1.Spring基础

框架 Core IoC容器AOP功能数据绑定类型转换等 TestingData AccessWeb Servlet 核心 IOC容器 定义 IoC&#xff08;Inversion of Control&#xff09;是控制反转的意思&#xff0c;这是一种面向对象编程的设计思想。 优点 在不采用这种思想的情况下&#xff0c;我们需要自…

51单片机-动态数码管显示

动态数码管显示就是在数码管中显示多个数字&#xff0c;利用了人眼的视觉差显示了数字&#xff0c;但是存在一个问题&#xff0c;在顺序执行显示数字和数值的设置时候&#xff0c;数码管的显示数据会出现错乱&#xff0c;因此&#xff0c;需要执行消影&#xff0c;每次在数码管…

机器学习速成第二集——监督学习之分类(理论部分)!

目录 分类算法的种类 分类问题的应用场景 模型选择与评估 结论 如何在不同数据集中选择最适合的监督学习分类算法&#xff1f; 监督学习中集成模型与单一模型相比有哪些具体的优势和劣势&#xff1f; 优势&#xff1a; 劣势&#xff1a; 在处理高维稀疏数据时&#xf…

AAAI论文截稿

标题&#xff1a;AAAI2025截稿不足36小时!抓住今年最后的机会&#xff01; 点击上方“会议之眼”关注 重磅干货&#xff0c;第一时间送达 会议之眼 快讯 AAAI&#xff08;Association for the Advancement of Artificial Intelligence&#xff09;全称国际顶级人工智能学术…

Promise 一个基于协程下的任务队列状态管理任务包 解决复杂的异步转同步问题

前言 一直都想写关于Promise的东西&#xff0c;Promise解决的问题特别多&#xff0c;而普通前端就把这东西结合ajax来做一个await request() ,如果仅仅作为这样一种东西使用那就太可惜了。 它是队列的任务包 前端同学应该是没听说过队列&#xff0c;但是大前端同学&#xff0…

Kubernetes-K8S

Kubernetes由于单词太长&#xff0c;省略掉中间8个字母简称为K8S。它介于应用服务和服务器之间。能够通过策略协调和管理多个服务&#xff0c;只需要一个YAML文件配置。定义应用的部署顺序等信息&#xff0c;自动部署应用到各个服务器&#xff0c;还可以自动扩容缩容。 架构原理…

RabbitMQ实现多线程处理接收消息

前言&#xff1a;在使用RabbitListener注解来指定消费方法的时候&#xff0c;默认情况是单线程去监听队列&#xff0c;但是这个如果在高并发的场景中会出现很多个任务&#xff0c;但是每次只消费一个消息&#xff0c;就会很缓慢。单线程处理消息容易引起消息处理缓慢&#xff0…

前程无忧 阿里227滑块 分析

声明: 本文章中所有内容仅供学习交流使用&#xff0c;不用于其他任何目的&#xff0c;抓包内容、敏感网址、数据接口等均已做脱敏处理&#xff0c;严禁用于商业用途和非法用途&#xff0c;否则由此产生的一切后果均与作者无关&#xff01;有相关问题请第一时间头像私信联系我删…

Qt下使用QtPdfium处理PDF文档

文章目录 前言一、使用QPdfDocument二、使用QtPdfium三、示例完整代码总结 前言 在我之前的文章中&#xff0c;有提到如何生成PDF&#xff0c;这个可以查看文末参考文章。如果要实现在Qt下进行PDF文档的处理&#xff08;读取显示&#xff09;&#xff0c;可以使用自带的PDF库以…

Unity(2022.3.38LTS) - 基础概念

目录 一. 场景 二. 游戏对象 三. 组件 四. 标签 五. 静态游戏对象 六. 保存 一. 场景 Unity 场景是游戏或应用开发中的一个重要概念。 Unity 场景的组成元素&#xff1a; 它通常包含了各种游戏对象&#xff0c;比如 3D 模型、灯光、摄像机、脚本组件、音频源等等。 作用…

串列翼无人机技术详解

串列翼无人机作为一种特殊布局的飞行器&#xff0c;其概念可追溯至早期航空探索时期。随着航空技术的不断进步&#xff0c;尤其是复合材料、先进控制算法及动力系统的革新&#xff0c;串列翼无人机逐渐从理论走向实践。这一设计初衷在于通过前后两组机翼的巧妙布局&#xff0c;…

Redis Cluster集群

redis-cluster集群 Redis Cluster是Redis官方提供的分布式解决方案。当遇到内存、并发、流量等瓶颈时&#xff0c;就可以采用Cluster架构达到负载均衡目的。 1.Redis单实例主要有单点故障&#xff0c;容量有限&#xff0c;流量压力上限的问题。 Redis单点故障&#xff0c;可以…

全开源智慧停车场微信小程序源码/智能停车系统源码/停车自助缴费系统/停车场管理收费+物业管理+物联网+自助缴费功能

源码简介&#xff1a; 智慧停车场微信小程序源码&#xff0c;全开源智能停车系统源码&#xff0c;停车自助缴费系统&#xff0c;具有停车场管理、停车收费、物业管理、物联网、自助缴费等多种功能。 这是一个全开源的智能停车系统&#xff0c;功能强大。它不仅能帮你管理停车…