C++ 面向对象(类和对象)—— 函数模板

C++ 面向对象(类和对象)—— 函数模板

在这里插入图片描述

🎁个人主页:工藤新一¹

🔍系列专栏:C++面向对象(类和对象篇)

​ 🌟心中的天空之城,终会照亮我前方的路

🎉欢迎大家点赞👍评论📝收藏⭐文章

文章目录

模板

  • 本阶段主要针对于==C++范式编程STL技术==进行详细讲解,探讨 C++ 更深层的应用template<class T> — 类模板
    template<typename T> — 函数模板

这两种模板的形式,在作用上是无差别的,唯一用法可能只是在于对函数模板和类模板的区分


一、模板简介

  1. 类型安全:模板提供了类型安全,因为编译器会在编译时检查类型错误。
  2. 代码重用:通过使用模板,你可以编写一次代码,然后对多种类型重用它,从而减少代码重复,提高复用性。
  3. 性能:模板在编译时实例化,这意味着没有运行时开销,性能通常比使用虚函数或动态类型识别(如 typeid)更好。

模板就是建立通用的摸具,大大提高复用性

在这里插入图片描述
  • 模板的特点:
    • 模板不可以直接使用,它只是一个框架
    • 模板的通用并不是万能的

二、函数模板

  • C++的另一种编程思想叫做泛型编程,主要利用的技术就是模板
  • C++提供两种模板机制:函数模板 类模板

2.1函数模板的基本语法

  • 函数模板的作用:​ 建立一个通用函数,其函数返回值类型和形参类型可以不具体制定,用一个虚拟类型来代表
C++voidfunc(int a);|| 返回值类型 形参类型 T T 使用虚拟类型T来代表 

语法:template <typename T> +后紧跟+ 函数声明或定义
紧跟的这一部分(函数声明或定义)就叫做函数模板

  • template ---> 声明创建模板
  • typename ---> 表示其后面的符号是一种数据类型,也可以使用class代替
  • T ---> 通用的数据类型,名称可替换

模板存在的意义:数据类型的参数化


  • 当我们打算实现两数交换,但我们并不明确两数的数据类型,我们可以写出无数种方式
C++ 内置数据类型 voidSwapInt(int& x,int& y){int temp = x; x = y; y = temp;}voidSwapDouble(double& x,double& y){double temp = x; x = y; y = temp;} 自定义数据类型 voidSwapPerson(Person& x, Person& y);...
  • 但是,我们发现,除返回值类型与参数的数据类型不同外,其余代码逻辑都是相同的
  • 于是函数模板就实现了

2.1.1函数模板的调用方式
C++//声明一个模板,告诉编译器T是一个通用的数据类型,及告诉编译器模板T后紧跟着的那段代码不要报错!template<typenameT>voidMy_Swap(T& x, T& y){ T temp = x; x = y; y = x;}--->模板的使用方式: intmain(){//1.自动类型推导My_Swap(参数1, 参数2);//2.显示指定类型(推荐 - 隐式类型转换) - 明确告诉编译器你想要的数据类型 My_Swap<数据类型>(参数1, 参数2);---><int>(a, b);}

2.2函数模板的注意事项

注意事项:

  • 自动类型推导,必须推导出一致的数据类型T
  • 模板必须要确定T的数据类型

1.自动类型推导,必须推导出一致的数据类型T

C++template<typenameT>voidMy_Swap(T& x, T& y){...}intmain(){int a =1, b =2;char c ='a';My_Swap(a, b);--->trueMy_Swap(a, c);--->falsereturn0;}
在这里插入图片描述

2.模板必须要确定T的数据类型

C++template<typenameT>voidfunc()--->函数体 func() 是函数模板,但没有指定模板参数 T 的具体类型 { cout<<"func() 的调用"<< endl;}intmain(){func();--->也没有在调用 func 时提供类型参数 //如果想调用func(),那么可以给模板显示指定一个数据类型 func<int>();--->这样,就可以调用到func()return0;}
在这里插入图片描述

2.4函数模板案例

案例描述:

  • 利用函数模板封装一个排序的函数,可以对不同的数据类型进行排序
  • 排序规则从大到小,排序算法为选择排序
  • 分别用char数组和int数组进行排序

1.逐一解决模板,首先我们需要先建立一个排序模板

#include<iostream>usingnamespace std;template<typenameT>voidMy_Sort(T arr[],int len){for(int i =0; i < len; i++){int pos = i;//默认当前i位置下标所对应的元素为最大元素for(int j = i +1; j < len; j++){if(arr[pos]< arr[j]) pos = j;}//当发现arr[i]不是最大值时,进行交换if(pos != i)My_Swap(arr[pos], arr[i]);}}voidtest01(){char chArr[]="udoijqwadcknzx";My_Sort(chArr,sizeof(chArr)/sizeof(char));}intmain(){test01();return0;}

2.在我们搭建排序模板的过程中,我们发现我们还需要一个支持两数交换的模板

C++template<typenameT>voidMy_Swap(T& x, T& y){ T temp = x; x = y; y = temp;}

3.当我们所有的算法步骤解决后,我们就可以再次利用模板的方式来输出结果了

C++//输出模板template<typenameT>voidMy_Print(T arr[],int len){for(int i =0; i < len; i++) cout << arr[i]<<" ";}

4.总代码:

#include<iostream>usingnamespace std;//交换模板template<typenameT>voidMy_Swap(T& x, T& y){ T temp = x; x = y; y = temp;}//排序模板 - 从大到小template<typenameT>voidMy_Sort(T arr[],int len){for(int i =0; i < len; i++){int pos = i;//默认当前i位置下标所对应的元素为最大元素for(int j = i +1; j < len; j++){if(arr[pos]< arr[j]) pos = j;}//当发现arr[i]不是最大值时,进行交换if(pos != i)My_Swap(arr[pos], arr[i]);}}//输出模板template<typenameT>voidMy_Print(T arr[],int len){for(int i =0; i < len; i++) cout << arr[i]<<" ";}voidtest01(){char charArr[]="dasjhdaxaodnqb";My_Sort(charArr,sizeof(charArr)/sizeof(char));My_Print(charArr,sizeof(charArr)/sizeof(char));}voidtest02(){int intArr[]={4,8,4,3,1,3,1,3,13,3,21,56,46};My_Sort(intArr,sizeof(intArr)/sizeof(int));My_Print(intArr,sizeof(intArr)/sizeof(int));}intmain(){test01(); cout << endl;test02();return0;}

2.5普通函数和函数模板的区别

普通函数与函数模板的区别(是否会发生隐式类型转换):

  • 普通函数调用时可以发生自动类型转换(隐式类型转换)
  • 函数模板调用时,如果利用自动类型推导,不会发生隐式转换
  • 如果利用显示指定类型的方式,可以发生隐式类型转换

2.5.1隐式类型转换

  • 在编程语言中,隐式类型转换是指在程序运行过程中,由编译器自动进行的类型转换,而不需要程序员显式地指定。例如,在C++中,如果将一个整数赋值给一个浮点数变量,编译器会自动将整数转换为浮点数。这种转换是隐式的,因为它是在程序员不知情的情况下由编译器完成的。

隐式类型转换的常见情况:

  • 数值类型之间的转换在**C++**中,当将一个较小范围的整数类型(如charshort)赋值给一个较大范围的整数类型(如int)时,会发生隐式类型转换。例如:
C++char c ='a';int i = c;// char类型隐式转换为int类型 输出结果97
C++intAdd(int x,int y)return x + y;voidtest(){int a =10;char c ='a';//发生隐式类型转换 cout <<Add(a, c)<< endl;--->109}

  • 函数模板- 无法推导出一致的数据类型
C++template<typenameT>voidMy_Add(T& x, T& y){return x + y;}intmain(){int a =10;char c ='c';//1.自动类型推导 cout <<My_Add(a, c);--->false//2.显示指定类型 cout << My_Add<int>(a, c);--->true,会发生隐式类型转换 return0;}
在这里插入图片描述
  • 总结:建议使用显示类型的方式,调用函数模板,因为可以自己确定通用类型

2.6普通函数和函数模板的调用规则

  • 调用规则如下:

​ 1.如果函数模板和普通函数都可以实现,优先调用普通函数

​ 2.可以通过空模板参数列表来强制调用函数模板

​ 3.函数模板也可以发生重载

​ 4.如果函数模板可以发生更好的匹配,优先调用函数模板

C++voidMy_Print(int a,int b)--->哪怕只有函数声明 voidMy_Print(int a,int b);{ 也无法直接调用不加空模板参数列表修饰的函数模板 cout <<"调用普通函数"<< endl;}template<typenameT>voidMy_Print(T& a, T& b){ cout <<"调用函数模板"<< endl;}intmain(){int a =10, b =20;My_Print(a, b);return0;}
在这里插入图片描述

  • 空模板参数列表:强制调用函数模板
C++ My_Print<>(a, b);
在这里插入图片描述

  • 函数模板也可以发生函数重载
C++template<typenameT>voidMy_Print(T& a, T& b){ cout <<"调用函数模板"<< endl;}template<typenameT>voidMy_Print(T& a, T& b, T& c){ cout <<"调用重载的函数模板"<< endl;}

  • 函数模板可以产生更好的匹配时,编译器会优先调用函数模板
C++voidMy_Print(int a,int b){ cout <<"调用普通函数"<< endl;}template<typenameT>voidMy_Print(T& a, T& b){ cout <<"调用函数模板"<< endl;}intmain(){char c1 ='a', c2 ='b';My_Print(c1, c2);return0;}
在这里插入图片描述
在这里插入图片描述
  • 因为编译器认为,调用普通函数时还需要进行隐式类型转换,那就过于麻烦了,所以就不去调用普通函数了。

总结:当我们使用函数模板时,最好减少使用普通函数,否则容易出现二义性


2.7模板的局限性

局限性:

  • 模板不是万能的

例如:

C++template<typenameT>voidf(T& a, T& b){ a = b;}

如上述代码中,提供一个赋值操作,如果 a 和 b 是一个数组,就无法实现了

再例如:

C++template<typenameT>voidf(T& a, T& b){if(a > b){...}}

如上述代码中,如果 T 的数据类型传入的是像 Pereson 这样的自定义数据类型,也无法正常运行

因此,C++为了解决这种问题,提供了模板的重载,可以为这些特定的数据类型提供具体化的模板

C++classPerson{public:Person(string Name,int Age){this->Name = Name;this->Age = Age;}public: string Name;int Age;};template<classT>boolMy_Compare(T& x, T& y){return x == y;}intmain(){ Person p1("啦啦啦",20); Person p2("呦呦呦",3); cout <<(My_Compare(p1, p2)?"p1 == p2":"p1 != p2")<< endl;return0;}
在这里插入图片描述

2.7.1运算符重载
  • 解决方法一:运算符重载
C++classPerson{public:booloperator==(const Person& other) cosnt{return Name == other.Name && Age == other.Age;}};

2.7.2具体化模板
  • 利用 Person具体化的模板实现代码,且这种基于具体化实现的模板会优先被调用
C++template<typenameT>boolMy_Compare(T& p1, T& p2)--->函数模板声明 {return p1 == p2;}template<>boolMy_Compare(Person& p1, Person& p2){return p1.Name == p2.Name && p1.Age == p2.Age;}
在这里插入图片描述
  • 相比于运算符重载模板具体化的优点:可以省去对每一个运算符都进行重载的过程,大大降低了代码量

总结:

  • 利用具体化的模板,可以解决自定义类型的通用化
  • 学习模板并不是为了写模板,而是为了在STL中能够运用系统提供的模板

🌟各位看官好,我是工藤新一¹呀~

🌈愿各位心中所想,终有所致!

🌟下一篇章类模板 ~~🌟

Read more

什么是 Session?Web 开发中 Session 的使用与注意事项

什么是 Session?Web 开发中 Session 的使用与注意事项

✅ 引言 在 Web 开发中,HTTP 协议是无状态的,这意味着每次请求之间没有关联。为了实现用户登录、购物车、权限控制等功能,服务器需要一种机制来“记住”用户。Session(会话) 就是解决这一问题的核心技术之一。 本文将深入讲解: * 什么是 Session? * Session 的工作原理 * 在 Java Web 和 Spring Boot 中如何使用 Session * 使用 Session 的最佳实践与常见注意事项 * 安全风险与应对策略 并提供完整的 Java + Spring Boot 示例代码,帮助你全面掌握 Session 的使用。 📌 一、什么是 Session? 1.1 基本定义 Session(会话)是服务器端用于保存用户状态的一种机制。

By Ne0inhk
【前端小站】HTML 标签:网页骨架,从空白到惊艳,全靠这些 HTML 标签搞事情

【前端小站】HTML 标签:网页骨架,从空白到惊艳,全靠这些 HTML 标签搞事情

半桔:个人主页  🔥 个人专栏: 《前端扫盲》《手撕面试算法》《C++从入门到入土》 🔖为什么有人总是赞美生活的丰富多彩?我想这是因为他们善于品尝生活中随时出现的意外。 -余华- 文章目录 * 前言 * 一. HTML结构 * 1.1 初始HTML标签 * 1.2 标签的层次 * 二. HTML文本标签 * 2.1 标题标签 * 2.2 段落标签 * 2.3 强调标签 * 2.3.1 加粗 * 2.3.2 倾斜 * 2.3.3 删除线 * 2.3.4 下划线 * 三. 媒体与交互标签 * 3.

By Ne0inhk
基于C++11手撸前端Promise

基于C++11手撸前端Promise

文章导航 * 引言 * 前端Promise的应用与优势 * 常见应用场景 * 并发请求 * Promise 解决的问题 * 手写 C++ Promise 实现 * 类结构与成员变量 * 构造函数 * resolve 方法 * reject 方法 * then 方法 * onCatch 方法 * 链式调用 * 使用示例 * `std::promise` 与 `CProimse` 对比 * 1. 基础功能对比 * 2. 实现细节对比 * (1) 状态管理 * (2) 回调注册与执行 * (3) 异步支持 * (4) 链式调用 * 3. 代码示例对比 * (1) `CProimse` 示例 * (2) `std::promise` 示例 * 4.

By Ne0inhk
❿⁄₁₃ ⟦ OSCP ⬖ 研记 ⟧ 密码攻击实践 ➱ 获取并破解Net-NTLMv2哈希(下)

❿⁄₁₃ ⟦ OSCP ⬖ 研记 ⟧ 密码攻击实践 ➱ 获取并破解Net-NTLMv2哈希(下)

郑重声明:本文所涉安全技术仅限用于合法研究与学习目的,严禁任何形式的非法利用。因不当使用所导致的一切法律与经济责任,本人概不负责。任何形式的转载均须明确标注原文出处,且不得用于商业目的。 🔋 点赞 | 能量注入 ❤️ 关注 | 信号锁定 🔔 收藏 | 数据归档 ⭐️ 评论 | 保持连接💬 🌌 立即前往 👉晖度丨安全视界🚀 ▶ 信息收集  ▶ 漏洞检测 ▶ 初始立足点  ▶ 权限提升 ▶ 横向移动 ➢ 密码攻击 ➢  获取并破解Net-NTLMv2哈希(下)🔥🔥🔥 ▶ 报告/分析 ▶ 教训/修复 目录 1.密码破解 1.1 破解Windows哈希实践 1.1.3 捕获Net-NTLMv2哈希实践 1.1.3.3 使用Netcat连接绑定 Shell(kali上) 1.连接流程 2.连接命令

By Ne0inhk