C++模板进阶

文章目录

  • 前言
  • 反向迭代器
    • 反向迭代器和正向迭代器的区别
    • stl反向迭代器源码
    • 反向迭代器模拟实现
    • 测试
  • 模板进阶
    • 非类型模板参数
      • Array
    • 模板的特化
    • 模板的分离编译

前言

模板进阶也没有到一些特别的东西,就是讲比较偏的一些特性。
在这里我们先来讲一下反向迭代器。

反向迭代器

反向迭代器和正向迭代器的区别

严格来说没有大的区别。真正的区别在于++或者- -的方向不一样。
它是倒着走的。

在这里插入图片描述

如果是list, 以我们自己的思路实现我们会怎么做?
我们可能有这样一个思路,我们把正向迭代器拷贝一份出来,名字变一下,其他保持不变。
再把反向迭代器的++变成node =node->prev; 就是增加一个类的思路。
这样行不行呢?我们可以测试一下。
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

我们再提供一个rbegin(), rend();
在这里插入图片描述

在这里插入图片描述

最后测试一下
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
好像还可以,就这么esay.但是还是太年轻太简单了。

我们普通人写的代码,还得看一下高手写的代码,在实践中进步

stl反向迭代器源码

我们看一下stl的源码。
它没有像我们前面一样实现一个类。

它的反向迭代器是这样实现的。
在这里插入图片描述

这怎么传了一个正向迭代器过去。
这是个类模板。

还得接着从源码中找。
用适配器。

发现stl极度的讨厌冗余重复的设计。

看源码。
也就是说你用一个正向迭代器就可以构造一个反向迭代器。
在这里插入图片描述

然后看这里++就是正向迭代器的- -, - -就是正向迭代器的+ +;

在这里插入图片描述

但是这里又有一个诡异的东西。
在这里插入图片描述
它的解引用没有取当前位置,它取了前一个位置。

正向和反向的区别来了。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

看结构就已经知道为解引用是取前一个位置

反向迭代器模拟实现

现在的思路跟之前不一样了,还是写一个类,但是我现在是用一个正向迭代器取构造一个反向迭代器。
现在重点在于反向迭代器的++是正向迭代器的- -;

template<class Iterator>
struct ReverseIterator
{typedef ReverseIterator<Iterator> Self;Iterator _cur;ReverseIterator(Iterator it):_cur(it){}Self& operator++(){--_cur;return *this;}Self& operator--(){++_cur;return *this;}//内核比较的还是指针bool operator!=(const Self& s){return _cur != s._cur;}
};

正向迭代器在物理上是个啥东西,它多大?
4个字节,就是个指针,只是自定义类型封装。
内置类型*it直接变成指令,直接调用那个地址的内容去解引用,自定义类型调用函数。

反向迭代器在物理上是多大?
也是四个字节。

类型的力量,都是4个字节存一个地址,但是三个不同的物种。

现在麻烦的东西来了。
在这里插入图片描述
返回前一个位置这很好处理,现在返回值是最难处理的。

这个位置需要T&或者const T&

可以看一下源码是怎么处理的,但是源码用的到东西远超我们目前的水平,所以我们不这样搞。
在这里插入图片描述

现在我们用一个简单的方式去解决,加两个模板参数就解决了。

template<class Iterator, class Ref, class Ptr>
typedef ReverseIterator<Iterator, Ref, Ptr> Self;
Ref operator*()
{Iterator tmp = _cur;--tmp;return *tmp;
}

至此,反向迭代器就成型了,我们就可以不用重复的方式了。

测试

接着我们在自己模拟实现的list当中测试一下。
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
好了。

两种实现方式的比较
关键点来了,好像这两种实现方式没有什么差别,都是实现一个类。

第一种方式,实现了一个正向迭代器,我把正向迭代器拷贝下来,再改一下就实现了。
第二种方式,第二种方式,用正向迭代器去初始化一个反向迭代器。

用正向迭代器去初始反向迭代器。那反向迭代器是不是包含一个正向迭代器对象。

最关键的地方来了。vector的反向迭代器怎么搞?
难道像第一种方式一样吗?不行,因为vector的迭代器本身就是内置类型,搞定。
但是第二种方式可以。

反向迭代器搞适配最重要的原因就是为了真正复用
给出list的正向迭代器可以适配出反向迭代器。那vector呢?

其实你实现除了一个反向迭代器,所以容器的反向迭代器都出来了
只要你有正向迭代器,你的正向迭代器能支持- -,也就是双向迭代器。
跟你的迭代器是原生指针还是自定义类型没有什么关系。

模板进阶

首先我们要把自己的视角打开一些,不能局限于只是类型的概念。
还有我们前面学到的适配器,仿函数这些。

我们前面讲的那些参数叫做模板参数,准确点叫做类型模板参数。
今天我们还要讲一个非类型模板参数。

非类型模板参数

这个非类型模板参数在什么地方有用呢?
假设我需要写一个静态的数组。以前的方式。
在这里插入图片描述

好像非常好,那假设N一个要10,一个要100是不是解决不了。

所以引入了非类型模板参数,它不是类型,它是常量,准确来说是整型常量。
在这里插入图片描述

// 类型模板参数
// 非类型模板参数 -- 整形常量
template<class T, int N = 20>
class Array
{
public:private:T _a[N];
};
int main()
{Array<int, 10> a1;Array<double, 20> a1;return 0;
}

所以它还是很方便。

它针对的是有些容器一开始就想去固定一些东西。

比如:
在这里插入图片描述
非类型模板只能是整型常量,而且只能是右值。
int, char,bool, long, long这些都属于整型家族。

用了非类型模板参数就不怎么需要typedef

Array

给大家看一个新东西,C11增加了一个新容器。
在这里插入图片描述
Array是一个静态数组,一个固定大小的顺序容器。

这东西怎么用呢?
在这里插入图片描述

C++设计出这个是为了对标vector吗?
它对标的是C语言的静态数组,它对比静态数组的优势是什么?
它对越界的检查更加严格
在这里插入图片描述

它怎样做到全面检查的?
很简单,你传的标识符是一个内置类型,它不需要解引用。
它是一个函数调用,它里面可以对这个参数进行检查。

这里有一个挺有意思的点,我为什么要用这个Araay.我直接用vector,还可以初始化。

vector<int> v(10, 0);
v[10];

唯一有点区别的就是array的空间是在栈上,以为它是直接开一个数组。
vector是在堆上,它要动态申请一下。
动态申请效率也很高,array在栈上,还可能把栈本来就不大,堆很大。

模板的特化

有些地方有需求,我们想对某些类型进行特殊处理。

比如我们比较日期类
在这里插入图片描述
在这里插入图片描述
但是这个比较好像并不满足我的需求
我还是希望按日期去比较但是它现在是日期的指针,日期的指针本身也是可以比较的,但是它比较的方式并不符合需求。我还是想要用实际的日期来比较。

那怎么办?
不用仿函数,还有别的方式吗?

我们可以用模板特化的语法。
在这里插入图片描述
在这里插入图片描述

这种写法的意思就是,你不用日期的指针就用原来那个。
你是日期的指针我就用特化的那个。

模板针对的是广泛的类型,但是我们可能想对某些类型进行特殊处理。

上面的函数模板是可以不用特化的
在这里插入图片描述

在这里插入图片描述
这两个是可以同时存在的,只是它跟模板实例化出来的函数构成函数重载。
刚才用的是特化,这个是匹配。

虽然好像可以用这个匹配替换掉特化,但是特化的用途还是非常大的。

再给大家看一个场景

这是我们我们讲仿函数时举过的一个例子。
在这里插入图片描述
在这里插入图片描述
我不想写这个仿函数,我有没有其他的方式。
我们可以用特化的方式。
在这里插入图片描述

Date*可以调用这个特化版本。
函数模板特化的意义好像确实不大,但是类模板很有意义。

再看一个应用场景

这是一个仿函数
在这里插入图片描述

在这里插入图片描述
特化除了我们之前的玩法,还有一个更宽泛的玩法。
之前专门特化了Date*,现在还能这样特化。
在这里插入图片描述
这个特化叫做偏特化,它和前面的特化有什么区别呢?
刚才的特化是针对T实例成具体的Date*,它是针对具体的类型。
现在对她进行进一步的限制,所以叫做偏特化。

它是针对指针这个泛类。
如果你写全特化,要写好几个。现在只要你是指针都可以解引用,我都不是用指针比较,我是用指针比较的对象比较。

特化就是要对某些类型处理,但不一定是具体的类型,它可以是泛的类型,比如指针。

1.当全特化和偏特化同时存在的时候走哪个?
肯定是走全特化,全特化是现成的…
2.当它们同时存在的时候有没有意义?
有一定的意义。

模板的分离编译

模板不支持分离编译,这个我们在前面的文章是有提到的。
我们这里说的不支持分离编译是在两个文件,声明在.h文件,定义在.cpp文件。

为什么?
模板会报链接错误。
fatal error
在这里插入图片描述

我们怎么来看待这个链接错误呢?
首先我们得先分析一下程序会发生什么。
我们还得搞懂为什么普通函数可以,函数模板不可以,或者普通类可以类模板不可以?

在这里插入图片描述

在这里插入图片描述
刚开始走的都是单线,后面两条线进行交互,除了合并还做一件很重要的事情,链接。
在这里插入图片描述

大家注意,func.i生成fun.o的时候生成一堆汇编指令,但是它只能生成func的,生不成Add的。
在这里插入图片描述

为什么它生不成Add的?
在这里插入图片描述

现在其实就已经可以解释为什么链接错误了,因为没有Add的地址。

怎么解决呢?
编译不通过就是找不到函数的地址,为什么找不到函数的地址呢?
不知道怎么实例化。
那告诉它怎么实例化就可以了

第一种解决方案
显示实例化
在这里插入图片描述

但是这样很不好用

更好的方式
第二种方式
可以声明和定义分离,但是不要分离到两个文件在这里插入图片描述

上面是函数模板,类模板呢?
可以参考一下stl的源码。
模板只定义在一个文件。

继续看其他的,比较短小的函数直接在类里面定义,为什么?
类里面默认就是类联,短小的适合类联。
比较长一点的就在类里面声明,类外面定义。

为什么我放在.h就不存在链接错误?
因为.h在函数调用的地方展开。
Add()只有声明,所以变成call(?),没有地址,然后链接的时候去找。
在这里插入图片描述

那这个函数有没有地址?
有,因为我既有声明又有定义。有定义就不需要找了,直接实例化,然后在当前函数就生成地址。

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

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

相关文章

java.net.SocketException: Connection reset

背景 在我用socket进行TCP通信的时候&#xff0c;当我关闭client端时在服务端出现了Connection reset的异常。 一、问题 下面是异常信息&#xff1a; Exception in thread "Thread-12" java.lang.RuntimeException: java.net.SocketException: Connection reseta…

解决下载huggingface模型权重无法下载的问题

文章目录 方法一(推荐)方法二方法三依然存在的问题 由于某些原因&#xff0c;huggingface的访问速度奇慢无比&#xff0c;对于一些模型(比如大语言模型LLM)的权重文件动辄几十上百G&#xff0c;如果用默认下载方式&#xff0c;很可能中断&#xff0c;这里推荐几种方式。 方法一…

ShopsN commentUpload 文件上传漏洞复现

0x01 产品简介 ShopsN 是一款符合企业级商用标准全功能的真正允许免费商业用途的开源网店全网系统。 0x02 漏洞概述 ShopsN commentUpload 接口处存在任意文件上传漏洞,攻击者可以利用文件上传漏洞执行恶意代码、写入后门、读取敏感文件,从而可能导致服务器受到攻击并被控…

cat EOF快速创建一个文件,并写入内容

在linux系统中&#xff0c;如果你有这个需求 vi一个文件 /etc/docker/daemon.json 在这个文件中写入内容 { "registry-mirrors": ["https://iw3lcsa3.mirror.aliyuncs.com","http://10.1.8.151:8082"],"insecure-registries":[&quo…

SaaS 电商设计 (五) 私有化部署-实现 binlog 中间件适配

一、 背景 具体的中间件私有化背景在上文 SaaS 电商设计 (二) 私有化部署-缓存中间件适配 已有做相关介绍.这里具体讨论的场景是通过解析mysql binlog 来实现mysql到其他数据源的同步.具体比如:在电商的解决方案业务流中经常有 ES 的使用场景,用以解决一些复杂的查询和搜索商品…

STM32 寄存器配置笔记——I2C 读写AT24C02 EEPROM

一、简介 本文主要介绍STM32F10xx系列如何使用软件模拟I2C总线读写AT24C02的EEPROM数据。 二、概述 I2C协议是一种用于同步、半双工、串行总线(由单片机时钟线、单数据交换器数据线组成)上的协议。规定了总线空闲状态、起始条件、停止条件、数据有效性、字节格式、响应确认信号…

MES系统物料计划协同:全面解析与应用

一、MES系统物料计划协同概述 MES系统物料计划协同是指通过MES系统对物料计划进行统一管理和协调&#xff0c;确保生产计划的顺利进行。通过将物料需求、采购、库存、生产和配送等环节进行有效集成&#xff0c;实现供应链的优化。这种协同方式有助于提高供应链的透明度和协同性…

焊盘:十字连接VS全覆盖 铺铜

在铺铜规则中&#xff0c;焊盘连接方式有两种&#xff1a; 十字连接 优点&#xff1a;较好焊接&#xff1a;因铺铜面积减少&#xff0c;温度下降速度降低&#xff0c;较好焊接&#xff0c;不易虚焊。 缺点&#xff1a;载流能力较弱&#xff1a;铺铜面积↓ → 载流能力↓全连接…

Leetcode—118.杨辉三角【简单】

2023每日刷题&#xff08;六十&#xff09; Leetcode—118.杨辉三角 实现代码 class Solution { public:vector<vector<int>> generate(int numRows) {vector<vector<int>> ans(numRows);for(int i 0; i < numRows; i) {ans[i].resize(i 1);ans…

算法训练营Day14

#Java #二叉树层次遍历 #反转二叉树 开源学习资料 二叉树的层次遍历&#xff1a;力扣题目链接 二叉树的层次遍历很好理解&#xff1a; 就是从根结点一层一层地往下遍历&#xff08;同一层&#xff0c;从左到右&#xff09;&#xff1a; 迭代的方式很好理解&#xff1a;就是…

用实例域代替序数

在Java中&#xff0c;枚举类型的ordinal()方法返回枚举常量的序数&#xff08;即其在枚举声明中的位置&#xff09;。在某些情况下&#xff0c;使用实例域&#xff08;instance field&#xff09;代替序数可能更加安全和易读。以下是一个示例&#xff0c;演示如何使用实例域代替…

mysql CREATE DATABASE

DROP DATABASE IF EXISTS zengwenfeng;CREATE DATABASE zengwenfeng DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;SET NAMES utf8mb4; SET FOREIGN_KEY_CHECKS 0;USE zengwenfeng; 脚本天天少这些&#xff0c;天天找这段&#xff01;

computed 和 watch 的奇妙世界:让数据驱动你的 Vue 应用(下)

&#x1f90d; 前端开发工程师&#xff08;主业&#xff09;、技术博主&#xff08;副业&#xff09;、已过CET6 &#x1f368; 阿珊和她的猫_CSDN个人主页 &#x1f560; 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 &#x1f35a; 蓝桥云课签约作者、已在蓝桥云…

TestSSLServer4.exe工具使用方法简单介绍(查SSL的加密版本SSL3或是TLS1.2)

一、工具使用方法介绍 工具使用方法参照&#xff1a;http://www.bolet.org/TestSSLServer/ 全篇英文看不懂&#xff0c;翻译了下&#xff0c;能用到的简单介绍如下&#xff1a; 将下载的TestSSLServer4.exe工具放到桌面上&#xff0c;CMD命令行进入到桌面目录&#xff0c;执…

从 0 开始创建 SpringBoot 项目

从 0 开始创建 SpringBoot 项目 从 0 开始创建 SpringBoot 项目环境准备创建项目项目目录结构及说明编写代码参考 从 0 开始创建 SpringBoot 项目 环境准备 操作系统&#xff1a;Windows 10IDE&#xff1a;IntelliJ IDEA 2023.3.1Java 版本&#xff1a;jdk1.8 工具网盘链接&…

俄罗斯军方计划用 Astra Linux 取代 Windows!

网络安全正在改变全球化的面貌&#xff0c;各国政府为了防范外国的间谍和破坏活动&#xff0c;正积极发展自己的技术。在这一趋势下&#xff0c;俄罗斯军方已经开始用 Linux 发行版 Astra Linux 替换 Windows 系统。 如何提高Linux系统安全性&#xff1f;提升Linux安全的关键策…

垃圾收集器及内存分配

目录 垃圾收集器种类 HotSpot虚拟机所包含的收集器 垃圾收集器部分源码 垃圾收集器后台日志参数说明与配对关系 1、串行垃圾收集器 串行垃圾收集器运行示意图 1&#xff09;、编写测试代码 2&#xff09;、设置垃圾回收为串行收集器 3&#xff09;、启动程序&#xff…

Flink 数据集类型

现实世界中&#xff0c;所有的数据都是以流式的形态产生的&#xff0c;不管是哪里产生的数据&#xff0c;在产生的过程中都是一条条地生成&#xff0c;最后经过了存储和转换处理&#xff0c;形成了各种类型的数据集。如下图所示&#xff0c;根据现实的数据产生方式和数据产生是…

基于JavaWeb+SSM+Vue微信小程序的移动学习平台系统的设计和实现

基于JavaWebSSMVue微信小程序的移动学习平台系统的设计和实现 源码获取入口Lun文目录前言主要技术系统设计功能截图订阅经典源码专栏Java项目精品实战案例《500套》 源码获取 源码获取入口 Lun文目录 第1章 绪论 1 1.1 课题背景 1 1.2 课题意义 1 1.3 研究内容 2 第2章 开发环…

【基础篇】1.1 认识STM32(二)

3.3 VREF/VREF-引脚 VREF和VREF-是STM32中用于提供参考电压的引脚。如下图&#xff1a; VREF引脚可以连接一个单独的外部参考电压&#xff0c;范围在2.0V&#xff5e;VDDA&#xff0c;但不能超过VDDA&#xff0c;否则就超过了模拟器件的最大供电电压。在100引脚的封装中&#…