【七】【C++】模版初阶

泛型编程

C++中的泛型编程是一种编程范式,它强调代码的重用性和类型独立性。通过泛型编程,你可以编写与特定数据类型无关的代码,使得相同的代码可以用于多种数据类型。

利用重载实现泛型编程

 
/*利用重载实现泛型编程*/
#include<iostream>
using namespace std;
void Swap(int& left,int& right){int temp=left;left=right;right=temp;}
void Swap(double& left,double& right){int temp=left;left=right;right=temp;}
void Swap(char& left,char& right){int temp=left;left=right;right=temp;}
//...

利用重载实现泛型编程的缺陷

代码冗余:

每个重载函数需要为每种类型单独编写,这会导致大量类似的代码。对于每个新类型,都需要添加一个新的重载版本,这使得代码难以维护。

可扩展性差:

如果需要支持新的类型,必须手动添加新的重载函数。这限制了代码的可扩展性,尤其是对于那些无法预先知道所有将要使用的类型的情况。

类型检查不够灵活:

重载依赖于编译时的静态类型检查。这意味着对于泛型代码,如果类型不匹配,编译器可能无法找到合适的重载,导致编译错误。

运行时效率:

重载函数通常会产生更多的运行时代码,因为每种类型的操作都是独立的函数。

泛型算法实现困难:

使用重载来实现真正的泛型算法非常困难。重载需要为每一种可能的类型组合提供一个实现。

维护和调试困难:

当有许多重载函数时,维护和调试变得更加困难。特别是当函数有多个参数,且每个参数都可能有多种类型时,重载的组合会急剧增加。

模版

在C++中,模板是实现泛型编程的一种强大工具,允许程序员编写与数据类型无关的代码。模板可以分为两种主要类型:函数模板和类模板。

函数模板

函数模板允许您编写处理不同类型的通用函数。它们在编译时根据提供的类型参数自动实例化。

 
/*函数模版*/
#include <iostream>
template <typename T>
T max(T x, T y) {return (x > y) ? x : y;}int main() {int a = 5, b = 10;std::cout << "Max of a and b: " << max(a, b) << std::endl;double c = 3.5, d = 4.5;std::cout << "Max of c and d: " << max(c, d) << std::endl;}

 
template <typename T>
T max(T x, T y) {return (x > y) ? x : y;}

template <typename T> 表示用T表示任意数据类型,后面紧接一个函数,称为函数模版。

在函数中可以使用T表示数据类型,使用函数时,会自动识别数据类型,自动编写对应函数。

类模板

类模板允许您创建可以处理任何数据类型的泛型类。实例化时,您指定特定的数据类型。

 
/*类模版*/
#include<iostream>
#include<stack>
#include<vector>
template <typename T>
class Stack {
private:std::vector<T> elements;public:void push(T const& elem) {elements.push_back(elem);}void pop() {if (elements.empty()) {throw std::out_of_range("Stack<>::pop(): empty stack");}elements.pop_back();}T top() const {if (elements.empty()) {throw std::out_of_range("Stack<>::top(): empty stack");}return elements.back();}bool empty() const {return elements.empty();}};int main() {Stack<int> intStack;intStack.push(7);std::cout << intStack.top() << std::endl;}

template <typename T>表示用T表示任意数据类型,后面紧接一个类,称为类模版。

在类中可以使用T表示数据类型,使用类的时候需要再后面加上<T代表的数据类型>。

Typename关键字

在C++中,关键字 typename 用于泛型编程,尤其是在模板编程中。它的主要作用是指示后续的标识符是一个类型名。这在两个主要场景中非常重要:模板参数的声明和模板内部的依赖类型名。

模板参数的声明

在定义模板时,typename 可用于声明一个类型模板参数:

 
template <typename T>
class MyClass {T data;// ...
};

在这里,typename T 表示 T 是一个类型参数,这意味着当你实例化 MyClass 时,你可以用任何类型替换 T

依赖类型名

在模板内部,当你引用一个依赖于模板参数的类型时,你需要在这个类型前使用 typename 来告诉编译器它是一个类型。这通常发生在模板的成员函数中:

 
template <typename T>
class MyClass {
public:typename T::SubType method();
};

在这个例子中,T::SubType 可能是一个类型,但在模板定义的时候编译器并不知道 T 会被什么替换,因此它无法确定 T::SubType 是一个类型还是其他东西。在这里使用 typename 告诉编译器 T::SubType 是一个类型。

关键字 classtypename 的可互换性

在模板参数的声明中,typenameclass 关键字是可以互换的,二者意味着相同的事情:

 
template <class T> // 同样有效
class MyClass {// ...
};

但是在依赖类型名的场景下,只能使用 typename

模版的原理

编写模板

当你编写一个函数模板时,你实际上是在定义一个函数的蓝图,而不是一个具体的、可以直接调用的函数。这个蓝图告诉编译器如何生成针对特定类型的函数实例。

 
template <typename T>
T max(T a, T b) {return a > b ? a : b;
}

在这个例子中,T 是一个占位符,代表任何类型。

编译器的实例化过程

当你调用一个模板函数时,编译器会查看你提供的参数类型,并根据这些类型,以及函数模板的定义,生成一个具体的函数实例。这个过程称为模板实例化。

 
int main() {auto result = max(5, 10); // 调用 max<int>(int, int)
}

在这个例子中,编译器看到你用两个整数调用了 max 函数,所以它生成了一个接受两个 int 类型参数的 max 函数的实例。

类型推导

C++11及以后的版本支持自动类型推导,这意味着在许多情况下,你不需要显式指定模板参数的类型;编译器可以从函数调用中的参数类型推导出来。

代码生成

一旦模板实例化完成,编译器就会生成与普通函数相同的机器码。这意味着使用函数模板不会比直接使用针对特定类型编写的函数有更多的运行时开销。

函数模版实例化

函数模板实例化是一个编译时过程,其中编译器根据模板函数被调用时提供的具体类型参数生成特定的函数实例。这个过程允许程序员编写一次模板代码,然后用不同的类型多次实例化,以适应不同的使用场景。

实例化过程

当编译器遇到一个模板函数调用时,它会检查提供给函数模板的实际类型参数。然后,编译器生成一个新的函数,其中模板参数被实际调用中使用的具体类型所替换。这个生成的函数就是模板的一个实例。

隐式实例化

在C++11及更高版本中,函数模板调用时往往不需要显式指定类型参数。编译器能够根据传递给函数的参数自动推导出模板参数的类型。这使得代码更简洁易读。

 
/*函数模版隐式实例化*/
#include <iostream>
using namespace std;
template<typename 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.2;cout << "Add(a1,a2):" << Add(a1, a2) << endl;cout << "Add(d1,d2):" << Add(d1, d2) << endl;}

显式实例化

虽然编译器通常能够自动实例化函数模板,但在某些情况下,可能需要或想要显式地指定模板的实例化。这可以通过提供模板参数的具体类型来完成。

 
/*函数模版显示实例化*/
#include <iostream>
using namespace std;
template<typename 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.2;cout << "Add(a1,a2):" << Add<int>(a1, a2) << endl;cout << "Add(d1,d2):" << Add<double>(d1, d2) << endl;}

函数模版参数匹配规则

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

 
/*函数模版显示实例化*/
#include <iostream>
using namespace std;
//通用加法函数
template<typename T>
T Add(const T& left, const T& right) {return left + right;}
//专门处理int的加法函数
int Add(const int& left, const int& right) {return left + right;}
int main() {Add(1,2);        //与非模版函数匹配,编译器不需要特化Add<int>(1,2);   //调用编译器特化的Add版本
}

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

 
/*函数模版精准匹配或生成匹配的函数*/
#include <iostream>
using namespace std;
//通用加法函数
template<typename T>
T Add(const T& left, const T& right) {return left + right;}
//专门处理int的加法函数
int Add(const int& left, const int& right) {return left + right;}
int main() {Add(1, 2);        //与非模版函数匹配,编译器不需要特化Add(1, 2.1);        //模版函数可以生成更加匹配的版本,编译器根据实参生成更加匹配的Add函数
}

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

类模版实例化

类模板实例化是C++中一种创建具体类从泛型类模板的过程。类模板通过允许类型作为参数,提供了一种强大的方式来编写灵活且可重用的代码。实例化过程中,模板参数被具体的类型替换,从而生成一个特定类型的类定义。

 
template <typename T>
class Box {
public:T contents;Box(T newValue) : contents(newValue) {}void show() const {std::cout << contents << std::endl;}
};

隐式实例化

在使用类模板时,你可以让编译器通过构造函数或方法的参数类型来自动推导模板参数类型。

 
Box box1(123); // 隐式实例化为 Box<int>
Box box2("C++"); // 隐式实例化为 Box<const char*>

显式实例化

也可以显式地指定模板参数的类型,明确地告诉编译器你想要实例化的具体类型。

 
Box<int> box1(123); // 显式实例化为 Box<int>
Box<std::string> box2("C++"); // 显式实例化为 Box<std::string>

使用实例化的类

一旦类模板被实例化,就可以像使用任何其他普通类一样使用它。你可以创建对象,调用方法等。

 
box1.show(); // 显示:123
box2.show(); // 显示:C++

结尾

最后,感谢您阅读我的文章,希望这些内容能够对您有所启发和帮助。如果您有任何问题或想要分享您的观点,请随时在评论区留言。

同时,不要忘记订阅我的博客以获取更多有趣的内容。在未来的文章中,我将继续探讨这个话题的不同方面,为您呈现更多深度和见解。

谢谢您的支持,期待与您在下一篇文章中再次相遇!

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

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

相关文章

四、Redis之配置文件

redis配置文件的名称 redis.conf 通过命令 find / -name redis.confvim redis.conf通过 : set nu 设置行号: set nonu 取消行号/关键字 搜索关键字: set noh 取消高亮选择4.1 Units 配置大小单位&#xff0c;开头定义了一些基本的度量单位&#xff0c;只支持 bytes&#…

第六十一天 服务攻防-中间件安全CVE复现K8SDockerJettyWebsphere

第61天 服务攻防-中间件安全&CVE复现&K8S&Docker&Jetty&Websphere 知识点&#xff1a; 中间件及框架列表&#xff1a; lIS,Apache,Nginx,Tomcat,Docker,Weblogic,JBoos,WebSphere,Jenkins, GlassFish,Jira,Struts2,Laravel,Solr,Shiro,Thinkphp,Sprng,Fl…

代码随想录 Leetcode131. 分割回文串

题目&#xff1a; 代码(首刷看解析 2024年2月3日&#xff09;&#xff1a; class Solution { public:vector<vector<string>> res;vector<string> path;bool isPalindrome(const string& s, int start, int end) {for (int i start, j end; i < j;…

获取真实 IP 地址(二):绕过 CDN(附链接)

一、DNS历史解析记录 DNS 历史解析记录指的是一个域名在过去的某个时间点上的DNS解析信息记录。这些记录包含了该域名过去使用的IP地址、MX记录&#xff08;邮件服务器&#xff09;、CNAME记录&#xff08;别名记录&#xff09;等 DNS 信息。DNS 历史记录对于网络管理员、安全研…

单臂路由实验(华为)

思科设备参考&#xff1a; 单臂路由实验&#xff08;思科&#xff09; 一&#xff0c;实验目的 在路由器的一个接口上通过配置子接口的方式&#xff0c;实现相互隔离的不同vlan之间互通。 ​ 二&#xff0c;设备配置 Switch1 <Huawei>sys [Huawei]vlan batch 10 20…

《计算机网络简易速速上手小册》第6章:网络性能优化(2024 最新版)

文章目录 6.1 带宽管理与 QoS - 让你的网络不再拥堵6.1.1 基础知识6.1.2 重点案例&#xff1a;提高远程办公的视频会议质量实现步骤环境准备Python 脚本示例注意事项 6.1.3 拓展案例1&#xff1a;智能家居系统的网络优化实现思路Python 脚本示例 6.1.4 拓展案例2&#xff1a;提…

STM32CAN2进入bus off 模式

工作遇到的问题记录 无人机CAN2整个进不了中断&#xff0c;通过查看寄存器判定出CAN节点进入了bus off mode 为何进入bus off &#xff0c;最后通过示波器看到整个CAN2总线波形就不对&#xff0c;总线出现了错误 Busoff的产生是一定是因为节点自身识别到自己发送错误&#xff…

Latex学习记录

目录 1.Latex各种箭头符号总结 2.[Latex]公式编辑&#xff0c;编号、对齐 3.Latex公式编号: 多行公式多编号&#xff0c;多行公式单编号 4.LaTex中输入空格以及换行 1.Latex各种箭头符号总结 箭头符号 - ➚ (piliapp.com)https://cn.piliapp.com/symbol/arrow/Latex各种箭头…

面试150 二进制求和 位运算

Problem: 67. 二进制求和 文章目录 思路复杂度Code 思路 &#x1f468;‍&#x1f3eb; 参考 复杂度 时间复杂度: O ( n ) O(n) O(n) 空间复杂度: O ( n ) O(n) O(n) Code class Solution {public String addBinary(String a, String b){StringBuilder ans new Stri…

STM32--USART串口(1)串口协议

一、通信接口 全双工&#xff1a;通信双方能够同时进行双向通信&#xff1b; 半双工&#xff1a;通信双方能够进行双向通信&#xff0c;但不能同时通信&#xff1b; 单工&#xff1a;只能从一个设备到另一个设备&#xff1b; 同步&#xff1a;接收方可以在时钟信号的指引下进…

Python爬虫某云免费音乐——多线程批量下载

重点一&#xff1a;每首音乐的下载地址 重点二&#xff1a;如何判断是免费音乐 重点三&#xff1a;如何用线程下载并保存 重点四&#xff1a;如何规避运行错误导致子线程死掉 重点五&#xff1a;如何管理子线程合理运行 需要全部代码的私信 运行效果&#xff1a; 歌手文…

睿尔曼6自由度机械臂ROS驱动包功能拓展之查询指令

1&#xff1a;主要环境预览 1&#xff1a;系统&#xff1a;Ubuntu 20.04 2&#xff1a;ROS&#xff1a;noetic 3&#xff1a;对于系统要求需根据相关手册完成机械臂相关依赖安装&#xff0c;能够运行机械臂本身基本功能&#xff0c; 包括 moveit。 4&#xff1a;准备资料…

指针详解(3)

各位少年&#xff0c;大家好&#xff0c;我是博主那一脸阳光&#xff0c;今天介绍 二级指针 指针数组&#xff0c;还有个指针数组模拟二维数组。 前言&#xff1a;在浩瀚的C语言编程宇宙中&#xff0c;指针犹如一把打开内存世界大门的独特钥匙&#xff0c;它不仅是理解程序运行…

PXIe-5842第三代PXI矢量信号收发器简介

内容 简介​PXIe-5842 VST的主要特性PXI VST软件工具PXI VST应用结论下一步 简介 NI于2012年引入了矢量信号收发器(VST)的概念。VST将RF信号发生器、RF信号分析仪和功能强大的FPGA集成在单个PXI模块上。PXIe-5842 VST是首款提供30 MHz到26.5 GHz连续频率覆盖范围的VST。PXIe…

RT-Thread线程管理(使用篇)

layout: post title: “RT-Thread线程管理” date: 2024-1-26 15:39:08 0800 tags: RT-Thread 线程管理(使用篇) 之后会做源码分析 线程是任务的载体&#xff0c;是RTT中最基本的调度单位。 线程执行时的运行环境称为上下文&#xff0c;具体来说就是各个变量和数据&#xff0c…

【云原生】docker-compose单机容器集群编排工具

目录 一、docker-compose容器编排的简介 二、docker-compose的使用 1、docker-compose的安装 2、docker-compose的配置模板文件yaml文件的编写 &#xff08;1&#xff09;布尔值类型 &#xff08;2&#xff09;字符串类型 &#xff08;3&#xff09;一个key有多个值 &am…

Qt应用开发(安卓篇)——调用ioctl、socket等C函数

一、前言 在 Qt for Android 中没办法像在嵌入式linux中一样直接使用 ioctl 等底层函数&#xff0c;这是因为因为 Android 平台的安全性和权限限制。 在 Android 中&#xff0c;访问设备硬件和系统资源需要特定的权限&#xff0c;并且需要通过 Android 系统提供的 API 来进行。…

单片机学习笔记---定时器/计数器(简述版!)

目录 定时器的介绍 定时计数器的定时原理 定时计数器的内部结构 两种控制寄存器 &#xff08;1&#xff09;工作方式寄存器TMOD &#xff08;2&#xff09;控制寄存器TCON 定时计数器的工作方式 方式0 方式1 方式2 方式3 定时器的配置步骤 第一步&#xff0c;对…

Github 上传项目(个人令牌token)

1.点击 github头像 &#xff1a; setting -> Developer Settings -> Personal access tokens 2.在要上传的文件夹下运行以下命令&#xff1a; git init git commit -m "first commit" git branch -M main 利用以下命令模…

【01】Linux 基本操作指令

带⭐的为重要指令 &#x1f308; 01、ls 展示当前目录下所有文件&#x1f308; 02、pwd 显示用户当前所在路径&#x1f308; 03、cd 进入指定目录&#x1f308; 04、touch 新建文件&#x1f308; 05、tree 以树形结构展示所有文件⭐ 06、mkdir 新建目录⭐ 07、rmdir 删除目录⭐…