C++之模版初阶(简单使用模版)

news2024/11/14 1:09:46

前言

        在学习C++的模版之前,咱们先来说一说模版的概念,模版在我们的日常生活中非常常见,比如我们要做一个ppt,我们会去在WPS找个ppt的模版,我们只需要写入内容即可;比如我们的数学公式,给公式套值,就可以算出结果;比如我们在写实验报告,老师会给一个实验报告的模版,我们按照里面的模版直接写入内容即可;所以生活中的模版就是相当于提供了一个事物的框架,我们只需要输入主要的内容就行了。那C++里面的模版也是如此,了解到了这里,就一起开始学习C++的模版吧!

一、函数模版

        我们在C++上学过一个函数重载,函数重载就是通过形参类型的不同,他们被分为不同的函数,虽然函数名相同;比如我们要实现一个交换函数,如下代码:

void Swap(int& a, int& b)
{
    int tmp = a;
    a = b;
    b = tmp;
}
void Swap(double& a, double& b)
{
    double tmp = a;
    a = b;
    b = tmp;
}
void Swap(char& a, char& b)
{
    char tmp = a;
    a = b;
    b = tmp;
}

        我们会发现,这作为一个交换函数未免也太麻烦了吧,虽然可以实现不同类型的参数的交换,但是这样的代码多少回=会显得冗余(多余),基于这样的原因,C++创造了函数模版。

1. 函数模版的定义

        函数模版代表了一个函数家族,该函数模板与类型无关,在使用时被参数化,根据实参类型产生函数的特定类型版本。

        如何理解这句话呢?其实就是我给你一个函数的模版,比如交换函数的模版,你只需要填入实参的不同类型,编译器自动识别,给你匹配相应的函数。

        其实这属于一种泛型编程,泛型编程指的就是编写与类型无关的通用代码,具有通用性,而模版是泛型编程的基础

2. 函数模版的格式

        template 指的是模版,要写函数模版之前,必须要指定模版的参数类型,也就是说T代表的就是模版,T就是可以换成任何数据类型的;而class / typename 是定义模版参数的关键字,可以在<>定义多个模版参数,用逗号分开即可。

        下面的代码,就是一个函数模版,我们要实现一个交换函数,那要设计的模版是谁呢?因为不同的类型都要实现交换,所以我们的模版参数要放到形参的位置上,让我们在传不同类型的实参时,可以顺利完成对应的交换。

#include <iostream>
using namespace std;
template<class T>
void Swap( T& left, T& right)
{
    T temp = left;
    left = right;
    right = temp;
}
int main()
{
    int a = 3;
    int b = 4;
    Swap(a, b);

    double c = 2.5;
    double d = 6.6;
    Swap(c, d);

    char ch1 = 'x';
    char ch2 = 'y';
    Swap(ch1, ch2);

    cout << a << ' ' << b << endl;
    cout << c << ' ' << d << endl;
    cout << ch1 << ' ' << ch2 << endl;
    return 0;
}

        在调用函数的时候直接传我们想交换的元素就可以,剩下的都是编译器做的事情。所以我们本来针对不同的数据类型要实现不同的函数重载,但有了函数模版之后,我们就只需要写一个函数了,剩下的是编译器帮我们完成的。不得不感慨一下,真的是懒人创造世界!

3. 函数模版的图示

        在编译器编译阶段,对于模板函数的使用,编译器需要根据传入的实参类型来推演生成对应类型的函数以供 调用。比如:当用double类型使用函数模板时,编译器通过对实参类型的推演,将T确定为double类型,然 后产生一份专门处理double类型的代码,对于字符类型也是如此。

4. 模版参数的实例化

函数模版的实例化,也就是相当于去调用这个函数模版,我们上面看到的是模版参数是在形参这里的,那如果我们没有在形参中使用模版参数呢?这样如何告诉模版参数是什么数据类型呢?所以在函数模版的实例化分为显式实例化和隐式实例化

4.1 隐式实例化

        隐式实例化就是并没有在函数调用的时候,明确指出模版参数的数据类型,比如下面的代码:

#include <iostream>
using namespace std;
template<class T>
void Swap( T& left, T& right)
{
    T temp = left;
    left = right;
    right = temp;
}
int main()
{
    int a = 3;
    int b = 4;
    Swap(a, b);
    return 0;
}

4.2 显式实例化

        显式实例化,就是在函数调用时,显式的写出了模版参数的数据类型,如下图代码:

#include <iostream>
using namespace std;
template<class T>
void Swap( T& left, T& right)
{
    T temp = left;
    left = right;
    right = temp;
}
int main()
{
    int a = 3;
    int b = 4;
    Swap<int>(a, b);
    return 0;
}

大家可能会觉得,这个显式实例化好像挺多余的,因为我们在函数传参的时候就已经隐式的传递了模版参数的数据类型了,那大家再来看下面的代码:

#include <iostream>
using namespace std;
template<class T>
T GetNum(int n)
{
    T b = n;
    return b;
}
int main()
{
    int a = 3;
    int b = 4;
    int c = GetNum(a);
    return 0;
}

如果我们没办法从传参这里让编译器得知模版参数的数据类型呢?我们这样写是不对的,所以我们不能隐式的传了。必须显式的写,正确的代码如下:

#include <iostream>
using namespace std;
template<class T>
T GetNum(int n)
{
    T b = n;
    return b;
}
int main()
{
    int a = 3;
    int b = 4;
    int c = GetNum<int>(a);
    return 0;
}

所以这种情况我们必须显式实例化,<>里面就是告诉模版参数的数据类型。

5. 如何应对不同数据类型的运算

如果我们要实现一个int和double进行加法,我们下面代码还可以实现吗?

#include <iostream>
using namespace std;
template<class T>
T Add(const T& a, const T& b)
{
    return a + b;
}
int main()
{
    int a = 3;
    double b = 4.7;
    cout << Add(a, b) << endl;
    return 0;
}

        我们要记住,对于模版函数,不允许类型转换的,所以a是int类型,先传入给T,T就是int类型,又因为不会类型转换,所以会报错的。那应该如何实现呢?我们直接给出最优方案:

#include <iostream>
using namespace std;
template<class T, class Y>
Y Add(const T& a, const Y& b)
{
    return a + b;
}
int main()
{
    int a = 3;
    double b = 4.7;
    cout << Add(a, b) << endl;
    return 0;
}

        我们只需要加一个模版参数就可以了,但是要知道返回值的类型必须是类型提升最后的类型,这是什么意思呢?

        因为不同类型的运算,会发生类型的提升

        char ——> int ——> float ——> double  

        所以要记住这里就OK了

6. 模版参数的匹配规则

1、 合适匹配的情况下,有现成的就匹配现成的
2、没有合适的,就将就用(指没有函数模版的时候)
3、有更合适就用更合适的,哪怕要自己使用函数模版创造

二、类模版

1. 类模版的格式

其实也跟函数模版差不多,就是我们要知道的是,模版参数给谁就行,其他的都不动。

template <class T>
class Stack
{
public:
    Stack(int capacity = 4)
        : _a(new T[capacity])
        , _size(0)
        , _capacity(capacity)
    {}
    ~Stack()
    {
        delete []_a;
        _a = nullptr;
        _size = _capacity = 0;
    }
private:
    T *_a;
    int _size;
    int _capacity;
};

2. 类模版的类名、类类型和类模版的实例化

        这里想说的是,我们定义了类模版之后,类名是什么?类类型是什么?类模版又是如何实例化的呢?

Stack<int> st;

上面的代码是正确调用这个类,那类名还是Stack,而这个类模版的类类型就不是类名类,

类名+显式实例化模版参数

#include <iostream>
using namespace std;
template <class T>
class Stack
{
public:
    Stack(int capacity = 4)
        : _a(new T[capacity])
        , _size(0)
        , _capacity(capacity)
    {}
    ~Stack()
    {
        delete []_a;
        _a = nullptr;
        _size = _capacity = 0;
    }
private:
    T *_a;
    int _size;
    int _capacity;
};
int main()
{
    Stack<int> st;
    return 0;
}

3. 在类模版外的函数定义

这里需要重点注意一下,我们函数定义可以在类内部,也可以在类外部,但是在类外部需要注意一下简单的规则。

1. 类模版外的函数定义,必须在同一个文件里

2. 需要指定域空间和模版

#include <iostream>
using namespace std;

template <class T>
class Stack
{
public:
    Stack(int capacity = 4)
            : _a(new T[capacity])
            , _size(0)
            , _capacity(capacity)
    {}
    ~Stack();
private:
    T *_a;
    int _size;
    int _capacity;
};

template<class T>
Stack<T>::~Stack()
{
    delete []_a;
    _a = nullptr;
    _size = _capacity = 0;
}

int main()
{
    Stack<int> st;
    return 0;
}

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

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

相关文章

Python监控服务进程及自启动服务方法与实践

1. 需求概述 当我们在Windows Server环境中部署XX系统的实际应用中&#xff0c;往往会遇到一些运维管理的挑战。为了确保系统的持续稳定运行&#xff0c;特别是在服务程序因各种原因突然关闭的情况下&#xff0c;我们可以借助Python的强大生态系统来构建一个监控与自动重启的管…

CBTC 2023氢能展倒计时6天,最新同期会议活动Plus版发布

随着时间的推移&#xff0c;CBTC2023深圳氢能技术展览会即将拉开序幕。这场盛会将于11月30日在深圳福田会展中心盛大开幕&#xff0c;以“以储赋能&#xff0c;智造未来”为主题&#xff0c;旨在搭建一个商务交流、供需合作、创新产品发布的平台&#xff0c;让氢能全产业链之间…

要想固态跑得稳,散热器也要够扎实,ORICO J-10 固态散热组合体验

我们在日常工作中&#xff0c;经常需要读写各种体积庞大的文件和数据&#xff0c;如果硬盘速度跟不上的话&#xff0c;工作效率就会大大降低。最近我发现M.2固态硬盘的价格已经大幅降低了&#xff0c;越来越多的国产品牌开始加入其中。所以我最近入手了一款ORICO J-10固态硬盘&…

HHDESK客户端连接导入导出

HHDESK客户端连接可以一键导出xlsx表格进行备份&#xff0c;也可一键导入设置连接。 1 导出 点击“资源”——“导出” 在弹出框中选择“保存”即可。 2 导入 右键“资源”——“导入” 点击“浏览”&#xff0c;选择相应的xlsx文档&#xff1b; 点击“确认”。 选择“…

一、TIDB基础

TIDB整个逻辑架构跟MYSQL类似&#xff0c;如下&#xff1a; TIDB集群&#xff1a;相当于MYSQL的数据库服务器&#xff0c;区别是MYSQL数据库服务器为单进程的&#xff0c;TIDB集群为分布式多进程的。 数据库&#xff1a;同MYSQL数据库&#xff0c;数据库属于集群&#xff0c;…

持续集成交付CICD:GitLabCI 通过trigger触发流水线

目录 一、理论 1.GitLabCI 二、实验 1.搭建共享库项目 2.GitLabCI 通过trigger触发流水线 三、问题 1.项目app02未触发项目app01 2.GitLab 报502网关错误 一、理论 1.GitLabCI (1) 概念 GitLab CI&#xff08;Continuous Integration&#xff09;是一种持续集成工具…

convertRect:toView 方法注意事项

这是在网上找到的一张图 我们开发中有时候会用到左边转换&#xff0c;convertRect:toView 通常情况下&#xff0c;我们回这样使用 CGRect newRect [a convertRect:originframe toView:c];其中newRect和 originframe的size相同&#xff0c;只改变origin newRect.origin a…

@ResponseBody详解

ResponseBody() 作用&#xff1a; responseBody注解的作用是将controller的方法返回的对象通过适当的转换器转换为指定的格式之后&#xff0c;写入到response对象的body区&#xff0c;通常用来返回JSON数据或者是XML数据。 位置&#xff1a; ResponseBody是作用在方法上的&…

AlphaPose-RKNN-rk3588

1. AlphaPose背景介绍 AlphaPose是一个用于人体姿态估计的开源工具。人体姿态估计在计算机视觉中是一个核心问题&#xff0c;它旨在定位并识别图像或视频中的人体关键点和骨骼结构。在许多应用中&#xff0c;如动作识别、行为分析、虚拟现实和增强现实&#xff0c;人体姿态估计…

解释PCIe MSI 中断要求中断向量连续?PCIe 规范里并没有明确指出

MSI 向量必须连续&#xff1f; 前言 MSI 物理条件&#xff0c;MSI 中断产生的逻辑是RC初始化的时候&#xff0c;由软件将配置写入到 EP 的 2 个寄存器中&#xff0c;这两个寄存器一个指示的是地址 Message Address&#xff0c;一个指示的是数据 Message Data。当 EP 试图触发…

MTK联发科MT6762/MT6763/MT6765安卓核心板参数规格比较

MT6762安卓核心板 MTK6762安卓核心板是一款工业级高性能、可运行 android9.0 操作系统的 4G智能模块。 CPU&#xff1a;4xCortex-A53 up to 2.0Ghz/4xCortex-A53 up to 1.5GhzGraphics&#xff1a;IMG GE8320 Up to 650MhzProcess&#xff1a;12nmMemory&#xff1a;1xLP3 9…

小学语文老师重点工作

小学语文老师是学生在语言学习过程中的关键引导者&#xff0c;他们的主要职责是帮助学生建立正确的语言基础&#xff0c;培养良好的阅读习惯&#xff0c;并提高学生的语文素养。以下是小学语文老师的一些重点工作。 一、教授语言知识 小学语文老师首要的任务是教授学生语言知识…

【深度学习】卷积神经网络结构组成与解释

卷积神经网络是以卷积层为主的深度网路结构&#xff0c;网络结构包括有卷积层、激活层、BN层、池化层、FC层、损失层等。卷积操作是对图像和滤波矩阵做内积&#xff08;元素相乘再求和&#xff09;的操作。 1. 卷积层 常见的卷积操作如下&#xff1a; 卷积操作解释图解标准卷…

LSTM模型预测时间序列:根据历史销量数据预测商品未来销量

经常会遇到一些需要预测的场景&#xff0c;比如预测品牌销售额&#xff0c;预测产品销量。 时间序列 今天分享一波使用 LSTM 进行端到端时间序列预测的完整代码和详细解释。 我们先来了解两个主题&#xff1a; 什么是时间序列分析&#xff1f; 什么是 LSTM&#xff1f; 时…

【nlp】3.6 Tansformer模型构建(编码器与解码器模块耦合)

Tansformer模型构建(编码器与解码器模块耦合) 1. 模型构建介绍2 编码器-解码器结构的代码实现3 Tansformer模型构建过程的代码实现4 小结1. 模型构建介绍 通过上面的小节, 我们已经完成了所有组成部分的实现, 接下来就来实现完整的编码器-解码器结构耦合. Transformer总体架…

leetcode刷题详解一

算法题常用API std::accumulate 函数原型&#xff1a; template< class InputIt, class T > T accumulate( InputIt first, InputIt last, T init );一般求和的&#xff0c;代码如下&#xff1a; int sum accumulate(vec.begin() , vec.end() , 0);详细用法参考 lo…

微信小程序富文本拓展rich-text

微信小程序富文本插件 功能介绍 支持解析<style>标签中的全局样式支持自定义默认的标签样式支持自动设置标题 若html中存在title标签,将自动把title标签的内容设置到页面的标题上,并在回调bindparse中返回,可以用于转发支持添加加载提示 可以在Parser标签内添加加载提…

Vue3-Pinia

Pinia是什么 Pinia是Vue的最新状态管理工具&#xff0c;是Vuex的替代品 比Vuex更大的优势在于&#xff1a; 1.提供更加简单的API&#xff08;去掉了mutation&#xff09; 2.提供符合&#xff0c;组合式风格的API&#xff08;和Vue3新语法统一&#xff09; 3.去掉了modules…

CS5511规格书|CS5511方案应用说明|DP转双路LVDS/eDP芯片方案

概述&#xff1a;CS5511是一个将DP/eDP输入转换为LVDS信号的桥接芯片&#xff0c;此外&#xff0c;CS5511可以用作在DP/eDP输入到DP/eDP输出场景中桥接芯片。CS5511的高级接收器支持VEDA DisplayPort&#xff08;DP&#xff09;1.3和嵌入式DisplayPort&#xff08;eDP&#xf…