【C++破局】泛型编程|函数模板|类模板

news2024/11/18 1:36:03

作者主页

📚lovewold少个r博客主页

   ⚠️本文重点c++模板初阶知识点讲解

👉【C-C++入门系列专栏】博客文章专栏传送门

😄每日一言:花有重开日,人无再少年

目录

前言

泛型编程

函数模板

函数模板概念

函数模板格式

函数模板的原理

函数模板的实例化

模板参数的匹配原则

类模板

类模板的定义格式

类模板的实例化

总结


前言

        C++是一门面向对象的语言,很多情况下我们不需要在编写程序时候去过多的考虑底层。而在前面我们学习C++的输入输出好像就是这样子,编译器会自动帮我们做很多的事情,而不需要自己去传递输入输出的变量类型等因素。这种方式肯定对于编写程序的人来讲是轻松的。世界上的各种科技的进步其实都离不开人对于懒惰的追求,对于方便的执着,而今天我要讲解的模板好像就算是一种特殊的产物。

        首先我们先写一个比较简单的程序来细致的探究一下我们为何需要模板。这里我们写了一个用于整形变量交换的函数,交换函数无论是在什么排序亦或者一些计算的时候都是常用函数。

void swap(int& x, int& y)
{
	int temp = x;
	x = y;
	y = temp;
}
int main()
{
	int a = 10;
	int b = 5;
	cout << "a=" << a << " " << "b=" << b << endl;
	swap(a, b);
	cout << "swap~" << endl;
	cout << "a=" << a << " " << "b=" << b << endl;
	return 0;
}

        问题来了,我们这里需要特别强调这里是用于整形类型的交换函数,因此这对于其他类型变量并不合适。那么我们如何去实现长整形,短整形,浮点型,字符型······。函数重载?

        C++提供了函数重载的方式,对于一些传参会直接以其传递参数而决定对应的函数。

        但是缺陷也很明显,我们的函数仅仅是类型不同,那么多类型我们都需要去重载么。亦或者我们先创建我们需要的类型,等到新类型出现的时候,用户再自己去增加对应的函数重载么。

        另一个关键点是,我们这里仅仅只是交换函数,比较简单,而对于一份各种函数相互嵌套的代码,一份代码出错,其他重载函数全得改,代码的可维护性比较低。

泛型编程

        我们再看先前的代码,会发现仅仅只是类型的不同罢了,我们可不可以提供一种方式和cout以及cin一样,把类型识别的任务交给编译器去完成,自己只需要给他传递参数变量即可。也就是说我们只需要提供一份代码作为模具,编译器可以根据不同的类型利用这个模具生成相应的代码。

void swap(int& x, int& y)
{
	int temp = x;
	x = y;
	y = temp;
}
void swap(double& x, double& y)
{
	double temp = x;
	x = y;
	y = temp;
}
void swap(char& x, char& y)
{
	char temp = x;
	x = y;
	y = temp;
}
int main()
{
	int a = 10;
	int b = 5;
	cout << "a=" << a << " " << "b=" << b << endl;
	swap(a, b);
	cout << "swap~" << endl;
	cout << "a=" << a << " " << "b=" << b << endl;
	return 0;
}

        无论是活字印刷术还是现在的模具浇筑技术,其根本的目的就是维持其功能一致即可,你可以注入不同的材料以改变其最后成品的效果,但本质上实现的功能是一样的,外形是一致的。

        之所以cv工程师能有独特的cv大法,也是因为前人拥有特定的已经可以使用的板子,而只需要去改吧改吧然后切合自己的工程内容即可实现一个全新的项目成果。这也就是一种代码复用的常规手段而已。

         话不多说,接下来直入正题。我们先谈一谈何为泛型编程。

        泛型编程是一种编程范式,其目标是编写与特定数据类型无关的通用代码,以便更广泛地重用代码。泛型编程使得程序员可以编写与数据类型无关的算法和数据结构,从而提高代码的灵活性、可重用性和可维护性。

        具体而言,泛型编程通过使用参数化类型(parameterized types)来实现。参数化类型是一种允许在代码中使用未指定具体类型的抽象类型。这样,可以编写算法和数据结构,而不必在编写时指定具体的数据类型。在需要使用这些算法和数据结构的地方,可以通过提供具体的类型来实现参数的具体化。

        在C++中,泛型编程主要通过模板来实现。模板允许程序员编写与数据类型无关的代码,可以用于不同的数据类型。这使得在不同的上下文中重用代码成为可能。例如,可以编写通用的排序算法、容器类、以及其他算法和数据结构,而不必为每种数据类型都编写一套特定的代码。而模板就是泛型编程的基础。

函数模板

函数模板概念

        函数模板是C++中用于创建通用函数的一种机制,允许程序员编写与特定数据类型无关的函数代码。函数模板通过使用参数化类型来实现,使得可以在编写代码时使用未指定具体类型的抽象类型这样,函数模板可以适用于多种数据类型,提高了代码的灵活性和重用性。

函数模板格式

template<typename T>
void Swap(T& x,T& y)
{
    T temp = x;
    x = y;
    y = temp;
}

template T 表示模板参数,T是一个占位符,代表任意数据类型。在函数模板中,可以使用T作为函数的参数类型、返回类型,以及在函数体中进行通用的操作。

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

函数模板的原理

        人类从农业时期到工业时期,很多重复机械的工作直接交给了机器去完成,极大的解放的生产力。机器生产淘汰了很多手工创作的东西,其本质上就是把这些工作交给了机器去完成。

        函数模板本身并不是一个函数,而是像类一样作为一个蓝图,是编译器用使用方式产生具体类型函数的一个模具。

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

函数模板的实例化

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

隐式实例化:让编译器根据传递的实参类型推演模板参数的实际类型。

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

int main()
{
	int a1 = 10;
	int a2 = 5;
	Add(a1, a2);

	double b1 = 10.0;
	double b2 = 5.0;
	Add(b1, b2);
	
	return 0;
}

        而当调用的时候将不同类型混合传参是不被允许的,因为在编译期间编译器需要推演,对于a1可以推演为int,b1可以推演为double,但是模板参数列表中只有一个T,编译器无法确定T被换为int还是double而报错。在模板中,编译器一般不会去做类型转换,否则出错了编译器就会承担不小的后果。

        因此对于这种问题我们通常有两种方式进行解决:用户自己进行强制类型转换或者使用显示实例化

显示实例化:在函数名的后面指定模板参数的实际类型

int main()
{
	int a1 = 10;
	int a2 = 5;
	Add(a1, a2);

	double b1 = 10.0;
	double b2 = 5.0;
	Add(b1, b2);
	Add<int>(a1, b1);//显示实例化
	return 0;
}

模板参数的匹配原则

        一个非模板函数和模板函数可以同时存在,而且该模板函数还可以实例化为这个非模板函数。(这种实例化为非模板函数指在参数传递上可能维持一样,但是依据优先匹配的原则,在调用函数的时候可以做一些特殊处理)。这里我们通过不同函数之间打印信息而进行调用优先级的查看。

//专门处理整形类型加法函数
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;
}
void test()
{
	Add(1,2);//与非模板函数优先匹配
	Add(1.0, 2.0);//模板函数
	Add<int>(1, 2);//调用特定版本的Add版本,走模板函数
}
int main()
{
	test();
	return 0;
}

        对于非模板函数与同名函数模板,如果有其他条件相同,在调用的时候会优先去调用非函数模板而不会从该函数模板中生成实例化函数,如果一个函数可以产生一个具有更好匹配的函数,那么选择模板。

int Add(int left, int right)
{
	cout << "非模板函数" << endl;
	return left + right;
}
//通用加法模板
template<class T1 , class T2>
T1 Add(T1 left, T2 right)
{
	cout << "模板函数" << endl;
	return left + right;
}
void test()
{
	Add(1, 2);//与非模板函数优先匹配
	Add(1, 2.0);//具备更加匹配的版本而不需要类型转换,编译器优先生成更加匹配的Add函数版本
}
int main()
{
	test();
	return 0;
}

        模板函数不能进行自动类型转换(帮你推演就够忙了,类型转换发生错误你还得骂他自然就不会帮你做这件没意义的事情)。但是普通函数可以进行自动类型转换

类模板

类模板的定义格式

template<class T1, class T2,class T3>//参数列表可以定义多个模板变量
class 类模板名
{
	//类成员定义
};

        我们前面学习顺序表提到过一点,使用typedef对类型名进行重命名。C语言中的类型重命名是指通过使用typedef关键字来为已有的类型创建一个新的别名。这样可以简化代码,提高可读性,并且方便批量修改具体类型,便于维护代码。但是当我们学习了类模板之后我们发现我们并不需要这样做,而是使用类模板。这里我们简要的构造一个动态顺序表的类模板来体会。

template<class T>
class Vector
{
public:
	Vector(size_t capacity = 10)
		: _pData(new T[capacity])
		, _size(0)
		, _capacity(capacity)
	{}

	~Vector();

	// 其他成员函数在这里...

	size_t Size() { return _size; };

	T& operator[](size_t pos)
	{
		assert(pos < _size);
		return _pData[pos];
	}

private:
	T* _pData;
	size_t _size;
	size_t _capacity;
};

// 类模板函数定义可以放在类定义外面。
template<class T>
Vector<T>::~Vector()
{
	if (_pData)
	{
		delete[] _pData;
	}
	_size = _capacity = 0;
}

类模板的实例化

        类模板的实例化与函数模板实例化不同,类模板实例化需要在类模板的名字前面跟<>,然后需要讲实例化的类型放在<>中即可(就像必须显式的实例化)。类模板名字不是真正的类,而实例化的结果才是真正的类(蓝图和用蓝图进行建筑的关系)。实例化时,编译器会生成针对具体数据类型的类定义,从而使得类模板变得具体化,可以像普通类一样使用。

Vector类名,Vector<int>才是类型
Vector<int> s1;
Vector<double> s2;

总结

        泛型编程是一种编程范式,其目标是编写可重用、通用的代码,以便能够适应多种数据类型而无需针对每种类型重复编写相似的代码。在C++中,泛型编程主要通过函数模板和类模板来实现。其优势如下:

  • 提高代码的重用性和可维护性。

  • 允许在不同数据类型上进行抽象,减少代码冗余。

函数模板(Function Templates)

概念:函数模板是一种定义通用函数的方式,其中函数的参数或返回类型可以是通用的类型参数。

语法:

template <class T> //typename也可以
T Add(T a, T b) 
{
    return a + b;
}

实例化:通过指定具体的数据类型,编译器会生成对应类型的函数定义。

实例化用法:

int r_int = Add(3, 4);          // 实例化为 Add<int>(3, 4)
double r_double = Add(3.14, 2.5);// 实例化为 Add<double>(3.14, 2.5)

类模板(Class Templates)

概念:类模板是一种定义通用类的方式,其中类的成员或行为可以依赖于通用的类型参数。

语法:
 

   template <class T> 
   class MyClass 
   {
   public:
       MyClass(T value) : data(value) {}
       void play() { /*  ... */ }
   private:
       T data;
   };

实例化:通过指定具体的数据类型,编译器会生成对应类型的类定义。

使用:

   MyClass<int> intObject(42);
   MyClass<double> doubleObject(3.14);

    作者水平有限,如有错误欢迎指正!


    

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

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

相关文章

我的一点记录 —— 256天

机缘 之所以开始坚持写博客&#xff0c;是希望可以借此对所学的知识进行一个巩固&#xff0c;并方便日后的复习。在CSDN这个平台&#xff0c;我也确实学到了很多有质量的内容&#xff0c;同时也希望自己可以向外输出高质量且有水平的相关知识。256天&#xff0c;蛮快的&#x…

基于被囊群算法优化概率神经网络PNN的分类预测 - 附代码

基于被囊群算法优化概率神经网络PNN的分类预测 - 附代码 文章目录 基于被囊群算法优化概率神经网络PNN的分类预测 - 附代码1.PNN网络概述2.变压器故障诊街系统相关背景2.1 模型建立 3.基于被囊群优化的PNN网络5.测试结果6.参考文献7.Matlab代码 摘要&#xff1a;针对PNN神经网络…

advanced-css: No.1

本套教程学习来自视频&#xff1a;https://www.bilibili.com/video/BV1n94y1o7yS/?p7&spm_id_frompageDriver&vd_sourceb79be8283df9418cb45941cc0bd583c6 案例 实现效果图 代码 HTML: <!DOCTYPE html> <html lang"en"><head><meta c…

【Unity之UI编程】玩法面板的实现

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 秩沅 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a;UI_…

Nacos热更新

Nacos热更新 相比其他注册中心&#xff0c;Nacos的优势之一在于热更新。 热更新&#xff0c;就是不需要重启服务&#xff0c;就能够更新配置。 nacos配置中心 首先&#xff0c;需要搭建 Nacos&#xff0c;详情见&#xff1a; https://www.cnblogs.com/expiator/p/17392549.h…

深度解剖Linux权限的概念

> 作者简介&#xff1a;დ旧言~&#xff0c;目前大二&#xff0c;现在学习Java&#xff0c;c&#xff0c;c&#xff0c;Python等 > 座右铭&#xff1a;松树千年终是朽&#xff0c;槿花一日自为荣。 > 目标&#xff1a;牢记Linux权限的概念。 > 毒鸡汤&#xff1a;你…

k8s笔记资源限制,亲和和性 污点和容忍

镜像下载失败 当宿主机资源不足时&#xff0c;会把pod kill &#xff0c;在其他node 重建 在宿主机放可能多的资源 requests(请求) limits(限制) 超出百分比 容器 pod namespace级别 pod使用资源过多&#xff0c;导致宿主机资源不足&#xff0c;会导致重建pod cpu 内存限…

通过Malloc 和 Free 的具体实现 加深对C指针 的理解(笔记)

【彻底搞懂C指针】Malloc 和 Free 的具体实现 https://danluu.com/malloc-tutorial/ 进程间的通信 : ①共享内存 ② 消息传递 &#xff08;内核实现&#xff09; 分配策略 (实现方面) by DUCK sbrk() malocal实现的主要函数 man sbrk 查看 数据结构 一个参考代码 https…

2.如何实现API统一响应-web组件篇

文章目录 1. 统一响应1.1 CommonResult 1. 统一响应 前端调用api接口获得统一的响应&#xff1a; 成功&#xff0c;返回成功的状态码和数据&#xff1b;失败&#xff0c;返回失败的状态码和错误提示。 在标准的 RESTful API 的定义&#xff0c;是推荐使用 HTTP 响应状态码 (…

PEFT概述:最先进的参数高效微调技术

了解参数高效微调技术&#xff0c;如LoRA&#xff0c;如何利用有限的计算资源对大型语言模型进行高效适应。 PEFT概述&#xff1a;最先进的参数高效微调技术 什么是PEFT什么是LoRA用例使用PEFT训练LLMs入门PEFT配置4位量化封装基础Transformer模型保存模型加载模型推理 结论 什…

Module build failed (from ./node_modules/postcss-loader/src/index.js):

出现该错误是你可能没认真看官网的安装配置&#xff0c;可直接看该目录3&#xff0c;一个字一个字看 先安装uview 如果选择v1版本&#xff0c;建议使用npm下载&#xff0c;下面以v1版本为例&#xff0c;使用的是npm下载&#xff0c;导入uview时该文件也在node_modules文件夹里…

常见后缀名总结 为你指点迷津

相信在日常的学习和工作中&#xff0c;大家一定会遇到各种各样的文件类型&#xff0c;他们的后缀名类型各不相同&#xff0c;诸多陌生的文件格式经常让大家不知道他们存在于电脑的意义&#xff0c;想删又没法删&#xff0c;想执行又无法执行。 今天&#xff0c;学长就带领大家一…

Linux学习第40天:Linux SPI 驱动实验(一):乾坤大挪移

Linux版本号4.1.15 芯片I.MX6ULL 大叔学Linux 品人间百味 思文短情长 主从工作方式完成数据交换&#xff0c;形象的说就是武侠中的乾坤大挪移。 本章实验的最终目的就是驱动 I.MX6UALPHA 开发板上的 ICM-20608 这个 SPI 接口的六轴传…

二十五、城市建成区结果制图——复杂图的制作

一、前言 有些时候看到一些参考文献中有些很复杂的图,例如多幅合并在一起,其实这种图本质上就是单一的图合并在一起,然后将其导出即可。 二、具体操作 其实对于制图必备要素的添加就不过多介绍,主要介绍有什么办法保持图形之间一致性,例如,其图例、指北针、比例尺统一…

着实不错的自适应大邻域搜索算法ALNS

文章目录 引言演进路线邻域搜索&#xff0c;NS变邻域搜素&#xff0c;VDNS大邻域搜索&#xff0c;LNS自适应大邻域搜索&#xff0c;ALNS 代码实现34个国内城市的TSP测试集XQF131 相关阅读 引言 之前介绍的差分进化算法和蚁群算法分别适用于求解连续优化问题和组合优化问题&…

Git基本概念和使用方式

Git 是一种版本控制系统&#xff0c;用于管理文件版本的变化。以下是其基本概念和使用方式&#xff1a; 仓库&#xff08;repository&#xff09;&#xff1a;Git 存储代码的地方&#xff0c;可以理解为一个项目的文件夹。提交&#xff08;commit&#xff09;&#xff1a;Git …

【OpenCV实现图像:用OpenCV图像处理技巧之白平衡算法2】

文章目录 概要Gray-world AlgotithmGround Truth Algorithm结论&#xff1a; 概要 随着数字图像处理技术的不断发展&#xff0c;白平衡算法成为了图像处理中一个关键的环节。白平衡的目标是校正图像中的颜色偏差&#xff0c;使得白色在图像中呈现真实的白色&#xff0c;从而提…

Linux之基础开发工具gdb调试器的使用(三)

文章目录 一、Linux调试器-gdb使用1、安装gdb2、背景3、Debug和release4、区分Debug和release 二、Linux调试器-gdb命令演示1、显示指定行之后的代码&#xff08;自动记录最后一条指令&#xff09;2、断点1、打印断点2、查看断点3、删除断点4、使能&#xff08;禁用/开启&#…

统计分钟级别的视频在线用户数+列炸裂+repeat函数

统计分钟级别的视频在线用户数 1、原始数据如下&#xff1a; uid vid starttime endtime select aa as uid,v00l as vid,2023-10-25 12:00 as starttime,2023-10-2512:15 as endtime union select bb as uid,v002 as vid,2023-10-25 12:05 as starttime,2023-10-25 12:19 …

笔记:AI量化策略开发流程-基于BigQuant平台(二)

五、模型训练股票预测 完成了数据处理&#xff0c;接下来就可利用平台集成的各算法进行模型训练和模型预测啦。本文将详细介绍“模型训练”、“模型预测”两大模块操作、原理。 模型训练和模型预测是AI策略区别于传统量化策略的核心&#xff0c;我们通过模型训练模块利用训练…