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

前言

        在学习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;
}

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

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

相关文章

DBS note5:Relational Algebra(关系代数)

目录 一、关系代数简介 二、Projection () 三、Selection () 四、Union () 五、Set Difference (-) 六、Intersection () 七、Cross Product () 八、Joins () 九、Rename () 十、Group By / Aggregation () 一、关系代数简介 关系代数中的所有运算符都接受一个关系并…

mac上Homebrew的安装与使用

打开终端&#xff1a;command空格 &#xff0c;搜索‘’终端 ’&#xff0c;打开终端 在终端中输入以下命令并按下回车键&#xff1a; /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"这个命令会自动下载并安装…

RocketMQ 消息中间件 知识点汇总

目录 RocketMQ1、什么是RocketMQ?常用术语:2、为什么需要消息队列3、什么是异步处理4、什么是服务解耦5、什么是流量控制6、消息队列两种模型队列模型:发布/订阅模型:总结:7、怎么保证消息不丢失8、如何处理消息被重复消费**出现消息重复的情况:****解决方法:**9、如何保…

github国内访问小解(windows)

git 下载安装 使用 github 前必须确保电脑上已经安装了 Git&#xff0c;可以从 Git 官方网站去下载。 官方的网站在国内访问会比较慢&#xff0c;这里可以选择国内镜像&#xff1a;https://registry.npmmirror.com/binary.html?pathgit-for-windows/ github 之旅 确认电脑已…

多功能PHP图床源码:Lsky Pro开源版v2.1 – 最新兰空图床

Lsky Pro是一款功能丰富的在线图片上传和管理工具&#xff0c;即兰空图床。它不仅可以作为个人云相册&#xff0c;还可以用作写作贴图库。 该程序的初始版本于2017年10月由ThinkPHP 5开发&#xff0c;经过多个版本的迭代&#xff0c;于2022年3月发布了全新的2.0版本。 Lsky Pro…

jQuery_02 引入jQuery,初试牛刀

引入jquery文件 我们在官网上点击dowmload那个 会发现进入了一个网页&#xff0c;里面全部是代码&#xff0c;你可能还在想为什么下载不了&#xff0c;其实jquery不跟vue一样&#xff0c;整个jquery就是一个js文件而已&#xff0c;所以直接ctrla 全选 ctrlc复制 &#xff0c;然…

PasteNow for mac剪贴板工具

PasteNow 是一款简单易用的剪贴板管理工具&#xff0c;可帮助用户快速存储和管理剪贴板上的文本和图片内容。用户可以使用 PasteNow 软件快速将文本内容保存到不同的笔记或页面中&#xff0c;也可以方便地将剪贴板上的图片保存到本地或分享给其他应用程序。 此外&#xff0c;P…

nodejs+vue+python+PHP+微信小程序-青云商场管理系统的设计与实现-安卓-计算机毕业设计

研究步骤、措施&#xff1a; &#xff08;1&#xff09;与指导老师确定系统主要功能&#xff1b; &#xff08;2&#xff09;做需求分析及功能模块划分&#xff1b; &#xff08;3&#xff09;指导老师通过后&#xff0c;设计出用例图&#xff0c;E-R图&#xff0c;功能模块图 …

Python开发运维:Django 4.2.7 使用Celery 5.3.5 完成异步和定时任务

目录 一、实验 1.Django使用Celery完成异步和定时任务 二、实验 1. 如何查看Django版本 一、实验 1.Django使用Celery完成异步和定时任务 (1)安装Django (2)新建Django项目 (3)初始框架 (4)urls.py引用视图views from django.contrib import admin from django.urls imp…

力扣每日一题-HTML实体解析器-2023.11.23

力扣每日一题&#xff1a;HTML实体解析器 开篇 这是今天的每日一题&#xff0c;中等难度&#xff0c;只要有耐心&#xff0c;应该是能够写出来的。下面给大家分享我的思路与代码。 题目链接: 1410.HTML实体解析器 题目描述 代码思路 1.创建一个哈希表&#xff0c;把要替换的…

基于SRGAN的人脸图像超分辨率

引言 SRGAN是第一个将GAN用在图像超分辨率上的模型。在这之前&#xff0c;超分辨率常用的损失是L1、L2这种像素损失&#xff0c;这使得模型倾向于学习到平均的结果&#xff0c;也就是给低分辨率图像增加“模糊的细节”。SRGAN引入GAN来解决这个问题。GAN可以生成“真实”的图像…

Windows IDEA Python开发环境安装+爬虫示例

文章目录 Python下载安装开发工具IDEA包管理安装pip基本用法从 requirements.txt 安装依赖 项目示例部署在 Linux 上安装Python在 Linux 上创建虚拟环境&#xff1a;安装依赖&#xff1a;运行你的爬虫 Python下载安装 Python 安装包下载地址&#xff1a;https://www.python.or…

nodejs微信小程序+python+PHP-书吧租阅管理系统的设计与实现-安卓-计算机毕业设计

目 录 摘 要 I ABSTRACT II 目 录 II 第1章 绪论 1 1.1背景及意义 1 1.2 国内外研究概况 1 1.3 研究的内容 1 第2章 相关技术 3 2.1 nodejs简介 4 2.2 express框架介绍 6 2.4 MySQL数据库 4 第3章 系统分析 5 3.1 需求分析 5 3.2 系统可行性分析 5 3.2.1技术可行性&#xff1a;…

Navicat 技术指引 | GaussDB 数据查看器

Navicat Premium&#xff08;16.2.8 Windows版或以上&#xff09; 已支持对GaussDB 主备版的管理和开发功能。它不仅具备轻松、便捷的可视化数据查看和编辑功能&#xff0c;还提供强大的高阶功能&#xff08;如模型、结构同步、协同合作、数据迁移等&#xff09;&#xff0c;这…

前端vue导出PPT,使用pptxgen.js

前言 公司新需求需要导出ppt给业务用&#xff0c;查阅资料后发现也挺简单的&#xff0c;记录一下。 如有不懂的可以留言&#xff01;&#xff01;&#xff01; 1.安装包 npm install pptxgenjs --save2.引入包 在需要使用的文件中引入 import Pptxgenfrom "pptxgenjs&…

对未来新能源车测试工具的看法

汽车行业正在经历变革的说法算是比较轻描淡写的了&#xff0c;还记得我1983年加入这个行业时&#xff0c;行业聚焦点是引入发动机管理系统。当时还是以家庭掀背车为主的时代&#xff0c;发动机分析仪的体积像衣柜一样大&#xff0c;还没出现“CAN”通信协议。现在经常听到我的导…

为什么选择美国VPS服务器

企业、个人和组织都需要一个稳定高效的服务器来托管他们的网站、应用程序和数据。而对于中国用户来说&#xff0c;寻找一个性价比高的便宜美国VPS服务器&#xff0c;既能满足需求&#xff0c;又能节约成本&#xff0c;成为了一个非常重要的问题。 VPS即虚拟专用服务器&#xf…

SuperMap iDesktopX如何获取简单线的起终端点及坐标

作者&#xff1a;超图研究院技术支持中心-于丁 SuperMap iDesktopX如何获取简单线的起终端点及坐标 在GIS行业应用中&#xff0c;线数据的端点坐标有非常多的用处。 定位和可视化&#xff1a;线数据端点坐标可以用于定位和可视化线要素在空间中的位置。这对于地图制作、规划和…

[PyTorch][chapter 64][强化学习-DQN]

前言&#xff1a; DQN 就是结合了深度学习和强化学习的一种算法&#xff0c;最初是 DeepMind 在 NIPS 2013年提出&#xff0c;它的核心利润包括马尔科夫决策链以及贝尔曼公式。 Q-learning的核心在于Q表格&#xff0c;通过建立Q表格来为行动提供指引&#xff0c;但这适用于状态…

nvm安装及使用

文章目录 一、[介绍](https://github.com/nvm-sh/nvm)1.1、卸载node1.1.1、从控制面板的程序卸载node1.1.2、删除node的安装目录1.1.3、查找.npmrc文件删除1.1.4、逐一删除下列文件1.1.5、删除node环境变量1.1.6、验证是否卸载成功 二、安装2.1、window系统2.2、mac系统2.2.1、…