【七】【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++

结尾

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

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

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

python中的可变与不可变、深拷贝和浅拷贝

个人猜想&#xff08;很遗憾失败了&#xff09; 在硬盘或者系统中存在一个字符集 如果存在硬盘中&#xff0c;那么硬盘出厂的时候他的字符集所占用的空间就已经确定了。 如果存在于系统的话&#xff0c;硬盘应该在出厂的时候为系统设置一个存储系统字符集的地方。在安装系统…

js获取文件名或文件后缀名(扩展名)的几种方法

有时候我们需要通过含有文件名和后缀名的一个字符串中提取出该文件的文件名或文件后缀名&#xff08;扩展名&#xff09;&#xff0c;可以通过如下几种方式进行截取。 例如文件名为: var fileName"12345.txt"; 方式一&#xff1a;subtring() 用法参考博文 【js截取字…

灵伴科技(Rokid)借助 Knative 实现 AI 应用云原生 Serverless 化

作者&#xff1a;朱炜栋、元毅、子白 公司介绍 Rokid 创立于 2014 年&#xff0c;是一家专注于人机交互技术的产品平台公司&#xff0c;2018 年即被评为国家高新技术企业。Rokid 作为行业的探索者、领跑者&#xff0c;目前致力于 AR 眼镜等软硬件产品的研发及以 YodaOS 操作系…

K8s 集群可观测性-数据分流最佳实践

简介 在微服务架构下&#xff0c;一个 k8s 集群中经常会部署多套业务&#xff0c;同时也意味着不同团队、不同角色、不同的业务会在同一集群中&#xff0c;需要将不同业务的数据在不同的空间进行管理和查看。 在传统的主机环境下&#xff0c;这个是可以通过不同的主机部署 Da…

力扣每日一题 ---- 1906. 查询差绝对值的最小值

本题中&#xff0c;我们的题目求的是差值的最小值&#xff0c;我们考虑一个因素&#xff0c;当前题目中给出的数组是没有排序过的&#xff0c;那么想要求的差值&#xff0c;是不是要两两配对进行判断差值最小值。这里我们就很费时间了&#xff0c; O(N^2)的时间复杂度&#xf…

学习笔记:超详解换根法(换根DP)(匠心之作)

一.换根DP的概念 1.换根DP是什么&#xff1f; 换根DP&#xff0c;又叫二次扫描&#xff0c;是树形DP的一种。 2.换根DP能解决什么问题&#xff1f; 换根DP能解决不指定根结点&#xff0c;并且根节点的变化会对一些值产生影响的问题。例如子结点深度和、点权和等。如果要 暴力…

【数据库】分区的优点和缺点

​ &#x1f34e;个人博客&#xff1a;个人主页 &#x1f3c6;个人专栏&#xff1a;数据库 ⛳️ 功不唐捐&#xff0c;玉汝于成 目录 前言 正文 优点&#xff1a; 缺点&#xff1a; 结语 我的其他博客 ​ 前言 数据库中的分区技术为处理大规模数据提供了一种有效的手段…

C++:输入流/输出流

C流类库简介 C为了克服C语言中的scanf和printf存在的缺点。&#xff0c;使用cin/cout控制输入/输出。 cin&#xff1a;表示标准输入的istream类对象&#xff0c;cin从终端读入数据。cout&#xff1a;表示标准输出的ostream类对象&#xff0c;cout向终端写数据。cerr&#xff…

如何远程操控vm虚拟机(finalshell版)

你是否因为虚拟机命令行操作不便而头疼&#xff1f;是否因为难以复制粘贴而烦恼&#xff1f;是否因为无法快速上传文件而烦躁&#xff1f; 别急&#xff01;现在有一个简单便捷的软件能够实现上述你所述说的所有烦恼&#xff0c;请听我细细道来~ 一、查看虚拟机的ip地址 a.首…

Unity C#高级特性 Partial 详细使用案例

文章目录 实例 1&#xff1a;分隔UI逻辑实例 2&#xff1a;Unity编辑器自动生成代码实例 3&#xff1a;数据模型分割实例 4&#xff1a;序列化扩展实例 5&#xff1a;多视图架构实例 6&#xff1a;Unity编辑器自定义 inspectors 在Unity中&#xff0c;部分类&#xff08;Partia…

Java多线程--生产者与消费者问题

文章目录 一、生产者与消费者问题&#xff08;1&#xff09;概要&#xff08;2&#xff09;案例1、案例描述及需要明确的问题2、整体框架构思3、生产者和消费者的数据共享问题4、对Clerk类里面方法的设计5、测试6、唤醒机制7、两个消费者 二、是否释放锁的操作&#xff08;1&am…

PMP备考的三个阶段及学习方法分享

PMP证书是项目管理必备的关键技能证书&#xff0c;是具备进行项目管理的重要技能体现。无论升职加薪&#xff0c;还是从事项目管理工作&#xff0c;都非常重要。 个人主要从事产品开发工作&#xff0c;开始逐渐承担一些项目经理角色&#xff0c;但目前项目管理知识薄弱&#x…

探讨深浅拷贝在js加密中的运用

深浅拷贝是JavaScript中常用的概念&#xff0c;用于复制对象或数组。它们在处理数据时有不同的用途&#xff0c;适用于不同的场景。在本文中&#xff0c;我们将详细介绍深浅拷贝的概念&#xff0c;提供案例代码&#xff0c;并探讨它们在JavaScript中的应用场景&#xff0c;以及…

@JsonFormat 和 @@DateTimeFormat 时间格式化注解详解(一篇带你解决问题)

前后数据交互过程中&#xff0c;Date类型的数据经常会出现类型映射转换的错误&#xff0c;为了达到业务的目标时间格式&#xff0c;通常会使用JsonFormat 和 DateTimeFormat&#xff0c;但是这两者有什么区别呢&#xff1f; 一、示例代码 先准备一个POJO&#xff0c;拥有Date类…

PPT录屏功能在哪?一键快速找到它!

在现代办公环境中&#xff0c;ppt的录屏功能日益受到关注&#xff0c;它不仅能帮助我们记录演示文稿的播放过程&#xff0c;还能将操作过程、游戏等内容完美录制下来。可是很多人不知道ppt录屏功能在哪&#xff0c;本文将为您介绍ppt录屏的打开方法&#xff0c;以帮助读者更好地…

本体论(ontology)在工业4.0中的应用

信息技术中的本体与哲学的本体论是不同的&#xff0c;它代表了某个专业领域的基本概念&#xff0c;它们在智能制造和工业4.0 中具有不可或缺的作用&#xff0c;为了实现人与机器&#xff0c;机器与机器之间的确定性操作。一个标准化的&#xff0c;精确定义的本体服务是非常重要…

【XR806开发板试用】xr806使用tcp socket与手机通信

本文为极术社区XR806开发板活动试用文章。 参考&#xff1a;基于星辰处理器的全志XR806开源鸿蒙开发板上手体验 搭建环境。并成功编译。 项目源码 &#xff1a; https://gitee.com/kingwho/smart-home 在同一个局域网中&#xff0c;手机与xr806连接后&#xff0c;手机 APP 每隔…

【开源】JAVA+Vue+SpringBoot实现就医保险管理系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 科室档案模块2.2 医生档案模块2.3 预约挂号模块2.4 我的挂号模块 三、系统展示四、核心代码4.1 用户查询全部医生4.2 新增医生4.3 查询科室4.4 新增号源4.5 预约号源 五、免责说明 一、摘要 1.1 项目介绍 基于JAVAVue…

【对象属性拷贝】⭐️按照需要转换的类型反射设置拷贝后对象的属性

背景&#xff1a; 小伙伴们大家好&#xff0c;最近开发的时候遇到一种情况&#xff0c;项目引入了全局序列化器来实现Date&#xff0c;LocalDateTime类型的字段根据时区转换&#xff0c;总体来说接口没什么要改动的&#xff0c;只要原来字段的属性是以上两种就行&#xff0c;但…

Linux/Uinx 系统编程:进程管理(3)

Linux/Uinx 系统编程&#xff1a;进程管理&#xff08;3&#xff09; 本章来讲解进程管理的最后一部分内容。 文章目录 Linux/Uinx 系统编程&#xff1a;进程管理&#xff08;3&#xff09;I/O重定向原理FILE结构体的内部结构重定向的实现过程 scanf 与 printfscanfprintf 重定…