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

作者主页

📚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.mzph.cn/news/165386.shtml

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

相关文章

用Java实现简易的图书管理系统(超详细)

目录 1.设计背景 2.设计思路 3.模块展示及代码演示 3.1 Book类的实现 3.2 BookList类的实现(书架) 3.3 异常类的实现(OperationException) 3.4 用户类的实现 3.5 操作接口的实现(定义规范) 3.6 操作类的具体实现 3.6.1 增加操作 3.6.2 查找操作 3.6.3 删除操作 3.6…

标签打印机打印标签时出现,数据处理过程中错误 无法设置项目 图片1的内容无法打印

环境&#xff1a; Win10专业版 NiceLabel Designer 10.1 问题描述&#xff1a; 标签打印机打印标签时出现&#xff0c;数据处理过程中错误 无法设置项目 图片1的内容无法打印 解决方案&#xff1a; 1.删除标签部分文字打印测试 还是一样&#xff08;未解决&#xff09; …

已解决java.lang.IllegalStateException异常的正确解决方法,亲测有效!!!

已解决java.lang.IllegalStateException异常的正确解决方法&#xff0c;亲测有效&#xff01;&#xff01;&#xff01;文章目录 报错问题解决思路解决方法交流 报错问题 java.lang.IllegalStateException 解决思路 java.lang.IllegalStateException通常表示程序的当前状态与…

jenkins 参数构建

应用保存 [rootjenkins-node1 .ssh]# ssh-keygen Generating public/private rsa key pair. Enter file in which to save the key (/root/.ssh/id_rsa): Enter passphrase (empty for no passphrase): Enter same passphrase again: Your identification has been saved i…

【嵌入式面试】2022年嵌入式经典面试题汇总(C语言)

&#x1f4dc;作者&#xff1a;不想脱发的基兄 &#x1f4fa;专栏&#xff1a;《嵌入式面试》 &#x1f4e3;格言&#xff1a;不管前方的路有多苦&#xff0c;只要走的方向正确&#xff0c;不管多么崎岖不平&#xff0c;都比站在原地更接近幸福。 前言&#xff1a; 2022年秋招我…

C++之初始化列表详细剖析

一、初始化列表定义 初始化列表&#xff1a;以一个冒号开始&#xff0c;接着是一个以逗号分隔的数据成员列表&#xff0c;每个"成员变量"后面跟一个放在括号中的初始值或表达式。 class Date { public:Date(int year, int month, int day): _year(year), _month(mont…

OpenCV快速入门:图像分析——图像分割和图像修复

文章目录 前言一、图像分割1.1 漫水填充法1.1.1 漫水填充法原理1.1.2 漫水填充法实现步骤1.1.3 代码实现 1.2 分水岭法1.2.1 分水岭法原理1.2.2 分水岭法实现步骤1.2.3 代码实现 1.3 GrabCut法1.3.1 GrabCut法原理1.3.2 GrabCut法实现步骤1.3.3 代码实现 1.4 Mean-Shift法1.4.1…

论文阅读 (106):Decoupling maxlogit for out-of-distribution detection (2023 CVPR)

文章目录 1 概述1.1 要点1.2 代码1.3 引用 2 预备知识3 方法3.1 MaxLogit3.2 改进MaxCosine和MaxNorm3.3 DML 1 概述 1.1 要点 题目&#xff1a;解耦最大logit分布外检测 (Decoupling maxlogit for out-of-distribution detection) 方法&#xff1a; 提出了一种心机基于log…

多级缓存快速上手

哈喽~大家好&#xff0c;这篇来看看多级缓存。 &#x1f947;个人主页&#xff1a;个人主页​​​​​ &#x1f948; 系列专栏&#xff1a;【微服务】 &#x1f949;与这篇相关的文章&#xff1a; JAVA进程和线程JAVA进程和线程-CSDN博客Http…

不做机器视觉工程师,转行,转岗的建议与想法

正所谓外行看热闹&#xff0c;内行看门道。提前咨询前辈们&#xff0c;多问问&#xff0c;多看看。要做就做&#xff0c;一定要提前做好防范。 无论你是要转行或者是转岗&#xff0c;看你有没有本钱和试错成本 有些人&#xff0c;家庭好&#xff0c;可以一直去试错和从头再来。…

无线WiFi安全渗透与攻防(国外篇):使用 Aircrack-ng 破解 WEP 密码

使用 Aircrack-ng 破解 WEP 密码 使用 Aircrack-ng 破解 WEP 密码一. 用 Aircrack-ng 破解 WEP 密码 - 背景知识网卡与网卡芯片WEP 加密协议WEP 所使用的身份认证协议二. 使用 Aircrack-ng 破解 WEP 密码 - 破解原理破解机理三. 使用 Aircrack-ng 破解 WEP 密码 - aircrack-ng …

学习.NET验证模块FluentValidation的基本用法(续1:其它常见用法)

FluentValidation模块支持链式验证方法调用&#xff0c;也就是说&#xff0c;除了 RuleFor(r > r.UserName).NotEmpty()调用方式之外&#xff0c;还可以将对单个属性的多种验证函数以链式调用方式串接起来&#xff0c;比如UserName属性不能为空&#xff0c;长度在5~10之间&a…

__attribute__((constructor))用法解析

__attribute__((constructor))是GCC和兼容的编译器中的一个特性&#xff0c;用于指示编译器将一个函数标记为在程序启动时自动执行的初始化函数。 同样的还有__attribute__((destructor))在main()函数后调用。 当你在一个函数声明或定义前加上__attribute__((constructor))属…

浅谈 Guava 中的 ImmutableMap.of 方法的坑

作者&#xff1a;明明如月学长&#xff0c; CSDN 博客专家&#xff0c;大厂高级 Java 工程师&#xff0c;《性能优化方法论》作者、《解锁大厂思维&#xff1a;剖析《阿里巴巴Java开发手册》》、《再学经典&#xff1a;《EffectiveJava》独家解析》专栏作者。 热门文章推荐&…

vue项目下.env.development环境变量配置文件

.env.development 文件是一个用于开发环境配置的文件。在许多应用程序中&#xff0c;开发环境和生产环境具有不同的配置需求。.env.development 文件允许你在开发环境中定义特定的环境变量和配置选项。 一般来说&#xff0c;.env.development 文件用于存储开发环境相关的配置信…

国自然项目基金撰写的隐藏技巧、范例分析及提交前的自我审查

目录 一、基金项目申请要求、重点及项目介绍 二、基金的撰写技巧 三、基金撰写的隐藏技巧 四、范例分析及提交前的自我审查 更多应用 基金项目申请需要进行跨学科的技术融合&#xff0c;申请人需要与不同领域结合&#xff0c;形成多学科交叉的研究。基金项目申请在新时期更…

由红黑树引出的HashMap扩容机制的思考

红黑树是什么&#xff1f; 三大特点&#xff1a; 根节点是黑色&#xff0c;叶节点是不存储数据的黑色空节点 任何相邻的两个节点不能同时为红色 任意节点到其可到达的节点间包含相同数量的黑色节点 联想&#xff1a;Java HashMap底层红黑树原理 HashMap基于哈希表Map接口实…

快速掌握Pyqt5的三种主窗口

PyQt5是一个强大的跨平台GUI框架&#xff0c;它提供了多种不同类型的主窗口类&#xff0c;以满足不同的应用需求。下面是PyQt5中最常见的几种主窗口类型及其创建方式的简介&#xff1a; 1. QMainWindow QMainWindow是用于创建具有菜单栏、工具栏、状态栏和中心窗口部件&#…

内存池 示例一

内存池是一种管理内存分配和释放的技术&#xff0c;用于优化内存的使用效率。它通过预先分配一块内存区域&#xff0c;并将其划分为多个较小的块&#xff08;内存块池&#xff09;&#xff0c;然后按需分配这些内存块来减少内存碎片化和频繁的系统调用。这些内存块可以是相同大…

Centos7.9配置nfs共享及rsync同步

客户需求对oracle数据库做一个跨机房的备份&#xff0c;原环境已做rman备份和每天expdp全库导出&#xff0c;远端只有虚拟化环境&#xff0c;可提供一个虚拟机&#xff0c;2个机房间网络互通。 首先配置nfs服务端 查看操作系统版本 [rootnas199 ~]# more /etc/redhat-relea…