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.rhkb.cn/news/220786.html

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

相关文章

Linux常用网络指令

网络参数设定使用的指令 手动/自动设定与启动/关闭 IP 参数&#xff1a;ifconfig, ifup, ifdown ifconfig ifconfig常用于修改网络配置以及查看网络参数的指令 [rootwww ~]# ifconfig {interface} {up|down} < 观察与启动接口 [rootwww ~]# ifconfig interface {options…

net6使用StackExchangeRedis实现分布式缓存

上一篇讲解了Redis的搭建及ServiceStack.Redis 与 StackExchange.Reids 的区别https://blog.csdn.net/qq_39569480/article/details/105249607 这篇文章遗我们来说下使用Microsoft.Extensions.Caching.StackExchangeRedis来对redis进行操作及帮助类。 首先在windows上安装red…

产品入门第六讲:Axure中继器

&#x1f4da;&#x1f4da; &#x1f3c5;我是默&#xff0c;一个在CSDN分享笔记的博主。&#x1f4da;&#x1f4da; ​​​​​​ &#x1f31f;在这里&#xff0c;我要推荐给大家我的专栏《Axure》。&#x1f3af;&#x1f3af; &#x1f680;无论你是编程小白&#xff0c…

Unity | Shader基础知识(第七集:案例<让图片和外部颜色叠加显示>)

目录 一、本节介绍 1 上集回顾 2 本节介绍 二、添加图片资源 三、 常用cg数据类型 1 float 2 bool 3 sampler 四、加入图片资源 五、使用图片资源 1 在通道里加入资源 2 使用图片和颜色叠加 2.1 2D纹理采样tex2D 2.2 组合颜色 六、全部代码 七、下集介绍 相关…

git-lfs基本知识讲解

目录 1. 基本知识2. 安装 1. 基本知识 git-lfs 是 Git Large File Storage 的缩写&#xff0c;是 Git 的一个扩展&#xff0c;用于处理大文件的版本控制。 它允许你有效地管理和存储大型二进制文件&#xff0c;而不会使 Git 仓库变得过大和不稳定。以下是一些与 git-lfs 相关…

【微服务】springboot整合minio详解

目录 一、前言 二、Minio 概述 2.1 Minio简介 2.1 Minio特点 三、Minio 环境搭建 3.1 部署过程 3.1.1 拉取镜像 3.1.2 启动容器 3.1.3 访问web页面 四、Minio基本使用 4.1 基本概念 4.2 上传文件演示 4.3 用户管理 4.4 Java操作Minio 4.4.1 导入依赖 4.4.2 上传…

机器学习——支持向量机

目录 一、基于最大间隔分隔数据 二、寻找最大间隔 1. 最大间隔 2. 拉格朗日乘子法 3. 对偶问题 三、SMO高效优化算法 四、软间隔 五、SMO算法实现 1. 简化版SMO算法 2. 完整版SMO算法 3. 可视化决策结果 六、核函数 1. 线性不可分——高维可分 2. 核函数 …

调用第三方http接口 hutool工具类

1、引入依赖 <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.8.0.M2</version> </dependency>2、请求组装 String params"<BSXml>" " <MsgHeader>&…

org.springframework.boot.autoconfigure.AutoConfiguration.imports新版自动配置

文章目录 场景 场景 springboot2.7.0之后的版本 自动配置方式有了变化, 新版兼容旧版 旧版新版META-INF/spring.factoriesMETA-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.importsConfigurationAutoConfiguration

【期末复习向】长江后浪推前浪之ChatGPT概述

参考文章&#xff1a;GPT系列模型技术路径演进-CSDN博客 这篇文章讲了之前称霸NLP领域的预训练模型bert&#xff0c;它是基于预训练理念&#xff0c;采用完形填空和下一句预测任务2个预训练任务完成特征的提取。当时很多的特定领域的NLP任务&#xff08;如情感分类&#xff0c…

STM32-UART-DMA HAL库缓冲收发

文章目录 1、说明1.1、注意事项&#xff1a;1.2、接收部分1.3、发送部分 2、代码2.1、初始化2.2、缓冲接收2.3、缓冲发送2.4、格式化打印 1、说明 1.1、注意事项&#xff1a; HAL库的DMA底层基本都会默认开启中断使能&#xff0c;如果在STM32CubeMx禁用了中断相关的功能&…

基于vue+element-plus+echarts制作动态绘图页面(柱状图,饼图和折线图)

前言 我们知道echarts是一个非常强大的绘图库&#xff0c;基于这个库&#xff0c;我们可以绘制出精美的图表。对于一张图来说&#xff0c;其实比较重要的就是配置项&#xff0c;填入不同的配置内容就可以呈现出不同的效果。 当然配置项中除了样式之外&#xff0c;最重要的就是…

07-抽象工厂

意图 提供一个创建一系列相关或相互依赖对象的接口&#xff0c;而无需指定它们具体的类。 适用性 在以下的情况可以选择使用抽象工厂模式&#xff1a; 一个系统要独立于它的产品的创建、组合和表示。一个系统要由多个产品系列中的一个来配置。要强调一系列相关的产品对象的…

linux驱动的学习 驱动开发初识

1 设备的概念 在学习驱动和其开发之前&#xff0c;首先要知道所谓驱动&#xff0c;其对象就是设备。 1.1 主设备号&次设备号&#xff1a; 在Linux中&#xff0c;各种设备都以文件的形式存在/dev目录下&#xff0c;称为设备文件。最上层的应用程序可以打开&#xff0c;关…

云原生之深入解析减少Docker镜像大小的优化技巧

一、什么是 Docker&#xff1f; Docker 是一种容器引擎&#xff0c;可以在容器内运行一段代码&#xff0c;Docker 镜像是在任何地方运行应用程序而无需担心应用程序依赖性的方式。要构建镜像&#xff0c;docker 使用一个名为 Dockerfile 的文件&#xff0c;Dockerfile 是一个包…

数据分析场景下,企业大模型选型的思路与建议

来源/作者&#xff1a;爱分析 随着大模型带来能力突破&#xff0c;让AI与数据分析相互结合&#xff0c;使分析结果更好支撑业务&#xff0c;促进企业内部数据价值释放&#xff0c;成为了当下企业用户尤为关注的话题。本次分享主要围绕数据分析场景下大模型底座的选型思路&#…

Kafka 安装与部署

目录 Kafka 下载 &#xff08;1&#xff09;将 kafka_2.11-2.4.1.tgz 上传至 /opt/software/ &#xff08;2&#xff09;解压安装包至 /opt/module/ [huweihadoop101 ~]$ cd /opt/software/ [huweihadoop101 software]$ tar -zxvf kafka_2.11-2.4.1.tgz -C ../module/&#…

什么是供应链安全及其工作原理?

6000公里长的丝绸之路将丝绸、谷物和其他货物从中国运送到帕尔米拉。尽管蒙古治下的和平保护丝绸之路免受海盗、强盗和内部盗窃的侵害&#xff0c;但商人仍然装备精良&#xff0c;并依赖于大型商队旅行和战略性放置的小型堡垒所提供的安全。 为什么供应链安全很重要&#xff1…

Content-Type是什么

目录 Content-Type是什么 获取方式 设置方式 常见类型 application/x-www-form-urlencoded multipart/form-data application/json text/xml text/html text/plain Content-Type是什么 Content-Type出现在请求标头和响应标头中&#xff0c;意思是内容类型&#xff0…

JAVA主流日志框架梳理学习及使用

前言&#xff1a;目前市面上有挺多JAVA的日志框架&#xff0c;比如JUL(JDK自带的日志框架),Log4j,Logback,Log4j2等&#xff0c;有人可能有疑问说还有slf4j&#xff0c;不过slf4j不是一种日志框架的具体实现&#xff0c;而是一种日志门面&#xff08;日志门面可以理解为是一种统…