【七】【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.hqwc.cn/news/449516.html

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

相关文章

部署实战--修改jar中的文件并重新打包成jar文件

一.jar文件 JAR 文件就是 Java Archive &#xff08; Java 档案文件&#xff09;&#xff0c;它是 Java 的一种文档格式JAR 文件与 ZIP 文件唯一的区别就是在 JAR 文件的内容中&#xff0c;多出了一个META-INF/MANIFEST.MF 文件META-INF/MANIFEST.MF 文件在生成 JAR 文件的时候…

STM32--USART串口(3)数据包

一、前言 在实际的工程中肯会有同时发送多种数据的情况&#xff0c;比如要不停的发送x、y、z分别对应三种不同的数据。xyzxyzxyz&#xff0c;但接收方可能是从中间某个地方开始接收的&#xff0c;这就导致数据错位。所以我们就需要将数据进行分割&#xff0c;打包成一个一个的…

spring问题点

1.事务 1.1.事务传播 同一个类中 事务A调非事务B B抛异常 AB事务生效&#xff08;具有传播性&#xff09; 同一个类中 事务A调非事务B A抛异常 AB事务生效 也就是主方法加了事务注解 则方法内调用的其他本类方法无需加事务注解&#xff0c; 发生异常时可以保证事务的回滚 最常…

LabVIEW核能设施监测

LabVIEW核能设施监测 在核能领域&#xff0c;确保设施运行的安全性和效率至关重要。LabVIEW通过与硬件的紧密集成&#xff0c;为高温气冷堆燃料装卸计数系统以及脉冲堆辐射剂量监测与数据管理系统提供了解决方案。这些系统不仅提高了监测和管理的精确度&#xff0c;也保证了核…

Java SWT Composite 绘画

Java SWT Composite 绘画 1 Java SWT2 Java 图形框架 AWT、Swing、SWT、JavaFX2.1 Java AWT (Abstract Window Toolkit)2.2 Java Swing2.3 Java SWT (Standard Widget Toolkit)2.4 Java JavaFX 3 比较和总结 1 Java SWT Java SWT&#xff08;Standard Widget Toolkit&#xff…

【毕业快刊】录用率98%!IF将破7,中科院2区,2个月录用,6天见刊,36天检索!

计算机类 ● 高分快刊 今天带来Elsevier旗下快刊解读&#xff0c;影响因子将破7&#xff0c;2023中科院分区上涨至中科院2区&#xff0c;期刊实力强劲&#xff0c;审稿快&#xff0c;实为毕业投稿首选&#xff0c;如有投稿意向可重点关注&#xff0c;具体详情见下文&#xff1…

不用苦苦寻觅!这是大模型检索增强生成(RAG)最全综述!

文章目录 一、背景二、摘要技术交流用通俗易懂的方式讲解系列三、RAG 框架3.1. Naive RAG3.1.1. Naive RAG 方案3.1.2. Naive RAG 不足 3.2. Advanced RAG3.2.1. Advanced RAG 方案3.2.2. Pre-Retrieval Process3.2.3. Post-Retrieval Process 3.3. Modular RAG3.3.1. Advanced…

【开源】JAVA+Vue.js实现学生日常行为评分管理系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、系统设计2.1 功能模块设计2.2.1 登录注册模块2.2.2 用户管理模块2.2.3 评分项目模块2.2.4 评分数据模块2.2.5 数据字典模块 2.3 可行性设计2.4 用例设计2.5 数据库设计2.5.1 整体 E-R 图2.5.2 用户2.5.3 评分项目2.5.4 评分数据2.5.…

Java实现视频抽帧

&#x1f913; 1️⃣配置Maven 在pox.xml中加入 <dependencies><dependency><groupId>org.bytedeco</groupId><artifactId>javacv-platform</artifactId><version>1.5.7</version></dependency> </dependencies…

短剧小程序开发:打造高效、便捷的娱乐体验

随着移动互联网的普及和用户需求的多样化&#xff0c;短剧小程序作为一种新型的应用形态&#xff0c;逐渐受到了广大用户的青睐。短剧小程序开发旨在为用户提供一种高效、便捷的娱乐体验&#xff0c;让用户在忙碌的生活中轻松享受到精彩的短剧内容。本文将探讨短剧小程序开发的…

卡诺图:逻辑相邻与几何相邻的统一

文章目录 1.一句话记住卡诺图2.卡诺图的由来、定义和特点3.填写卡诺图&#xff08;用卡诺图表示逻辑函数&#xff09;⑴根据真值表填写卡诺图⑵根据最小项&#xff08;或最大项&#xff09;填写卡诺图⑶根据函数的与或表达式填写卡诺图 4.用卡诺图化简逻辑函数⑴化简步骤⑵画圈…

如何部署Node.js服务并实现无公网ip远程访问本地项目【内网穿透】

文章目录 前言1.安装Node.js环境2.创建node.js服务3. 访问node.js 服务4.内网穿透4.1 安装配置cpolar内网穿透4.2 创建隧道映射本地端口 5.固定公网地址 前言 Node.js 是能够在服务器端运行 JavaScript 的开放源代码、跨平台运行环境。Node.js 由 OpenJS Foundation&#xff0…