C++:模版进阶 | Priority_queue的模拟实现
创作不易,感谢三连支持
一、非类型模版参数
模板参数分类为类型形参与非类型形参。
类型形参即:出现在模板参数列表中,跟在class或者typename之类的参数类型名称。
非类型形参,就是用一个常量作为类(函数)模板的一个参数,在类(函数)模板中可将该参数当成常量来使用。
注意:
非类型的模板参数必须在编译期就能确认结果。(分离编译会讲解)
我们来介绍一个c++11引入的array
array的底层其实封装的是一个静态数组。并且用到了非类型形参,在这里指代的是底层静态数组的容量大小。
思考:
1、为什么要有这个非模版形参??define定义宏常量难道不香吗??
define定义宏常量有时也可以解决问题,但是宏常量的作用域是全局,比如我们想让一个数组是10的容量,一个数组是20的容量,显然是做不到的,但是模版是可以做到的!!我们不传的时候N就是缺省值,传的时候就是我们指定的容量。
2、我直接用静态数组不行吗?为什么非得用类把他封起来??
(1)为了增加检查的功能,我们知道vs对于静态数组的越界功能是抽查行为,因此不是很安全,而我们把他封装在类里面,这样就可以去写个断言来防止数组越界
(2)[ ]的重载不仅可以增加检查功能,还可以去控制静态数组内容是否可以被写,同时还可以利用这个类去增加许多新的接口
3.能够作为非类型模版参数的有哪些类型??
只能是和int相似类型的才行,比如char、short、int、long int ……浮点数类对象以及字符串是不允许作为非类型模板参数的。
二、模版的特化
通常情况下,使用模板可以实现一些与类型无关的代码,但对于一些特殊类型的可能会得到一些错误的结果,需要特殊处理,比如:实现了一个专门用来进行小于比较的函数模板
可以看到,Less绝对多数情况下都可以正常比较,但是在特殊场景下就得到错误的结果。上述示例中,p1指向的d1显然大于p2指向的d2对象,但是Less内部并没有比较p1和p2指向的对象内容,而比较的是p1和p2指针的地址,这就无法达到预期而错误。
此时,就需要对模板进行特化。即:在原模板类的基础上,针对特殊类型所进行特殊化的实现方式。模板特化中分为函数模板特化与类模板特化。
2.1 函数模版特化
函数模板的特化步骤:
1. 必须要先有一个基础的函数模板
2. 关键字template后面接一对空的尖括号<>
3. 函数名后跟一对尖括号,尖括号中指定需要特化的类型
4. 函数形参表: 必须要和模板函数的基础参数类型完全相同,如果不同编译器可能会报一些奇怪的错误。
我们展示一下用法:
相当于是我们特殊化了一个版本出来,这个版本可以去比较指针解引用的内容!
但是我们还有这样一个方法——利用函数匹配的规则,直接把这个特殊类型的函数给出来
我们可以把函数模版当成是冰箱里的菜,模版特化的函数当成是预制菜,最后这个简单函数是现成菜。当我们有现成的吃的时候,就不会考虑去吃没做过的菜。这其实就是函数匹配规则!
并且这种函数实现简单明了,代码的可读性高,容易书写,因为对于一些参数类型复杂的函数模板,特化时特别给出,因此函数模板不建议特化。
2.2 类模版特化
函数有匹配规则,所以其实不怎么依赖特化,但是类并没有匹配规则啊!!所以特化最广泛的使用是在类中。类模版特化的步骤和函数模版特化的步骤是相似的。
2.2.1 全特化
全特化即是将模板参数列表中所有的参数都确定化
2.2.2 部分特化
部分特化的意思就是说,我们把其中一部分参数进行特化了。
2.2.3 偏特化(非常重要)
偏特化并不仅仅是指特化部分参数,而是针对模板参数更进一步的条件限制所设计出来的一个特化版本。
比如偏特化为指针类型(比较常见),或者是偏特化为引用类型(不常见)。
后面讲到仿函数的时候会做更进一步的介绍
三、模版分离编译
一个程序(项目)由若干个源文件共同实现,而每个源文件单独编译生成目标文件,最后将所有目标文件链接起来形成单一的可执行文件的过程称为分离编译模式。
一般来说,在我们书写大项工程的时候,为了保证代码的简洁性,我们常常将函数声明放在一个头文件里,将函数定义放在一个源文件里,然后再用另外一个源文件去进行测试。但是在模版这个地方,分离编译成为了一个难题!
3.1 模版的分离编译
假如有以下场景,模板的声明与定义分离开,在头文件中进行声明,源文件中完成定义:
// flby.h
template<class T>
T Add(const T& left, const T& right);
// flby.cpp
#include"flby.h"
template<class T>
T Add(const T& left, const T& right)
{
return left + right;
}
// test.cpp
#include"flby.h"
int main()
{
Add(1, 2);
Add(1.0, 2.0);
return 0;
}
为什么会这样呢??
3.2 解决方法
方法一:将声明和定义放到一个文件 "xxx.hpp" 里面或者xxx.h其实也是可以的。
因为最后都会在测试文件里面展开,这样编译的过程就可以进行实例化生成函数。一般比较推荐使用这种。
方法二:模板定义的位置显式实例化。这种方法不实用,不推荐使用。
显式实例化的意思就是,你不是推断不出来吗??那我就直接告诉你要生成什么样的函数!
四、模版的总结
优点:
1. 模板复用了代码,节省资源,更快的迭代开发,C++的标准模板库(STL)因此而产生
2. 增强了代码的灵活性
缺陷:
1. 模板会导致代码膨胀问题,也会导致编译时间变长(需要推导并生成实例化函数)
2. 出现模板编译错误时,错误信息非常凌乱,不易定位错误
五、priority_queue的介绍
priority_queue的文档介绍
1. 优先队列是一种容器适配器,根据严格的弱排序标准,它的第一个元素总是它所包含的元素中最大(小)的。
2. 此上下文类似于堆,在堆中可以随时插入元素,并且只能检索最大堆元素(优先队列中位于顶部的元素)。
3. 优先队列被实现为容器适配器,容器适配器即将特定容器类封装作为其底层容器类,queue提供一组特定的成员函数来访问其元素。元素从特定容器的“尾部”弹出,其称为优先队列的顶部。
4. 底层容器可以是任何标准容器类模板,也可以是其他特定设计的容器类。容器应该可以通过随机访问迭代器访问,并支持以下操作:
empty():检测容器是否为空
size():返回容器中有效元素个数
front():返回容器中第一个元素的引用
push_back():在容器尾部插入元素
pop_back():删除容器尾部元素
5. 标准容器类vector和deque满足这些需求。默认情况下,如果没有为特定的priority_queue类实例化指定容器类,则使用vector。
6. 需要支持随机访问迭代器,以便始终在内部保持堆结构。容器适配器通过在需要时自动调用算法函数make_heap、push_heap和pop_heap来自动完成此操作。
其实优先级队列就是我们数据结构里的堆!!
DS:二叉树的顺序结构及堆的实现_顺序打印堆-CSDN博客
大家可以看看博主的这篇博客,堆主要的应用就是解决top-k问题,在这篇文章里有具体的分析。
六、priority_queue的模拟实现
//仿函数
template<class T>
struct less //冰箱里的菜
{bool operator()(const T& x, const T& y) const{return x < y;}
};
template<class T>
struct greater
{bool operator()(const T& x, const T& y) const{return x > y;}
};
template<class T, class Container = vector<T>, class Compare = less<T>>//默认是建大堆class priority_queue{public:void adjust_up(int child){int parent = (child - 1) / 2;while (child > 0){if (Compare()(_con[parent], _con[child]))//_con[child] > _con[parent]{std::swap(_con[child], _con[parent]);child = parent;parent = (child - 1) / 2;}elsebreak;}}void adjust_down(int parent){int child = parent * 2 + 1;//建大堆,找大,假设左孩子比右孩子大while (child < _con.size()){if (child + 1 < _con.size() && (Compare()(_con[child], _con[child + 1])))//_con[child] < _con[child + 1]++child;if (Compare()(_con[parent], _con[child]))//_con[parent] < _con[child]{std::swap(_con[parent],_con[child]);parent = child;child = parent * 2 + 1;}elsebreak;}}bool empty() const{return _con.empty();}size_t size() const{return _con.size();}const T& top() const{return _con[0];}void push(const T& val){_con.push_back(val);//插入后要向上调整adjust_up(_con.size() - 1);//向上调整算法}void pop(){std::swap(_con[0], _con[_con.size() - 1]);_con.pop_back();adjust_down(0);//向下调整算法}void swap(priority_queue<T>& q){_con.swap(q._con);}private:Container _con;};
这边我们用到了两个仿函数,如果是建大堆的话,用less<T>,如果是建小堆的话,用greater<T>,仿函数就是通过重载()的行为,模拟出函数的效果,比函数指针会易用很多
七、模版特化的深入分析
假设我们放了一个日期类进去,能进行比较吗??
答案是可以的,前提是我们需要在日期类重载出比较操作符
如果是以下这种情况呢??
虽然指针也可以比较大小,但是指针比较大小的方式显然不符合我们的预期,我们希望的是比较指针解引用的内容,这个时候应该怎么办呢?
第一个方法:再写一个专门用来比较指针解引用的仿函数
struct PDateless{bool operator()(const Date* p1, const Date* p2){return *p1 < *p2;}};struct PDateGreater{bool operator()(const Date* p1, const Date* p2){return *p1 > *p2;}};
第二个方法:对原来的less和greater进行全特化出一个专门比较Date指针类型的仿函数
//全特化版本(因为全特化了,只能针对Data*) (即食菜)template<>struct less<Date*>{bool operator()(const Date* x, const Date* y) const{return *x < *y;}};
//全特化版本(因为全特化了,只能针对Data*)
template<>
struct greater<Date*>
{bool operator()(const Date* x, const Date* y) const{return *x > *y;}
};
但是以上两种方法是不是都不太好,因为无论是全特化还是写一个全新的仿函数,我们都只是针对了Date*类型,但是如果我们以后遇到int* double*……难道也要再写一个吗??所以就有了一个更好的方法。
方法三:对原来的less和greater进行偏特化限制出一个专门比较任意指针类型的仿函数
//偏特化版本 具体类型,针对指针这个泛类 必须在原来的基础之上 (预制菜)
template<class T>
struct less<T*>
{bool operator()(const T* x, const T* y) const{return *x < *y;}
};
//偏特化版本 (针对所有类型的指针)
template<class T>
struct greater<T*>
{bool operator()(const T* x, const T* y) const{return *x > *y;}
};
我们这个时候发现,偏特化可以帮助我们更好地解决指针比较这样的问题。
给大家举个形象的比喻,模版就相当于是冰箱里的菜,全特化版本就相当于是即食菜,而偏特化就相当于是预制菜。重新写一个特定的仿函数就相当于是外卖
外卖>即食菜>预制菜>冰箱里的菜。
1、 即食菜和预制菜理论上来说都是通过冰箱里的菜走出来的,所以必须要先有模版才能进行全特化和偏特化。
2、从前往后优先级变低。
相关文章:

C++:模版进阶 | Priority_queue的模拟实现
创作不易,感谢三连支持 一、非类型模版参数 模板参数分类为类型形参与非类型形参。 类型形参即:出现在模板参数列表中,跟在class或者typename之类的参数类型名称。 非类型形参,就是用一个常量作为类(函数)模板的一个参数&…...

【刷题记录】详谈设计循环队列
下题目为个人的刷题记录,在本节博客中我将详细谈论设计循环队列的思路,并给出代码,有需要借鉴即可。 题目:LINK 循环队列是线性表吗?或者说循环队列是线性结构吗? 对于这个问题,我们来看一下线…...

人工智能|机器学习——k-近邻算法(KNN分类算法)
1.简介 k-最近邻算法,也称为 kNN 或 k-NN,是一种非参数、有监督的学习分类器,它使用邻近度对单个数据点的分组进行分类或预测。虽然它可以用于回归问题,但它通常用作分类算法,假设可以在彼此附近找到相似点。 对于分类…...

乐得瑞 1C to 2C快充线:引领充电数据线新潮流,高效快充解决接口难题
随着科技的不断进步,数据线的接口种类也日渐繁多,但在早些时候,三合一和二合一的数据线因其独特的设计而备受欢迎。这类数据线通常采用USB-A口作为输入端,并集成了Micro USB、Lightning以及USB-C三种接口,满足了当时市…...

O2OA(翱途)开发平台如何在流程表单中使用基于Vue的ElementUI组件?
本文主要介绍如何在O2OA中进行审批流程表单或者工作流表单设计,O2OA主要采用拖拽可视化开发的方式完成流程表单的设计和配置,不需要过多的代码编写,业务人员可以直接进行修改操作。 在流程表单设计界面,可以在左边的工具栏找到Ele…...

0 OpenHarmony开源鸿蒙NEXT星河版内核嵌入式编程
开源鸿蒙NEXT星河版内核嵌入式编程 作者将狼才鲸创建日期2024-03-08 CSDN文章阅读地址Gitee文章下载地址 一、前景提要 2024年1月18日,华为放出HarmonyOS NEXT 鸿蒙星河版开发者预览版本(不是HarmonyOS NEXT版,是HarmonyOS NEXT星河版&…...

Vue | 基于 vue-admin-template 项目的跨域问题解决方法
目录 一、现存问题 二、解决方法 2.1 修改的第一个地方 2.2 修改的第二个地方 2.3 修改的第三个地方 自存 一、现存问题 报错截图如下: 二、解决方法 2.1 修改的第一个地方 在 .env.development 文件中: # base api # VUE_APP_BASE_API /d…...

mutex 和 channel 哪一个工作效率更高?
关于Rust中mutex和channel哪一个工作效率更高的问题,实际上并没有一个绝对的答案,因为效率的高低取决于具体的使用场景和需求。 互斥锁(mutex)主要用于保护共享资源,确保一次只有一个线程可以访问它。当需要多个线程同…...

Elasticsearch 通过索引阻塞实现数据保护深入解析
Elasticsearch 是一种强大的搜索和分析引擎,被广泛用于各种应用中,以其强大的全文搜索能力而著称。 不过,在日常管理 Elasticsearch 时,我们经常需要对索引进行保护,以防止数据被意外修改或删除,特别是在进…...

备考银行科技岗刷题笔记(持续更新版)
银行考试计算机部分复习 IEEE 802.11的帧格式 1.1 IEEE 802.11是什么? 802.11是国际电工电子工程学会(IEEE)为无线局域网络制定的标准。目前在802.11的基础上开发出了802.11a、802.11b、802.11g、802.11n、802.11ac。并且为了保证802.11更…...

代码随想录算法训练营第五十五天|583. 两个字符串的删除操作、72. 编辑距离。
583. 两个字符串的删除操作 题目链接:两个字符串的删除操作 题目描述: 给定两个单词 word1 和 word2 ,返回使得 word1 和 word2 相同所需的最小步数。 每步 可以删除任意一个字符串中的一个字符。 解题思路: 1、确定dp数组&#x…...

Softmax 回归 + 损失函数 + 图片分类数据集【动手学深度学习v2】李沐动手学深度学习课程笔记
目录 Softmax回归 损失函数 图片分类数据集 Softmax回归从零开始实现 Softmax回归简洁实现 Softmax回归 回归和分类的区别 回归问题举例上节课的预测房价问题,分类问题就是对样本进行分类 回归和分类的具体区别 假设真实的类别为第i个类别(值为1&#x…...

git 初始化项目并上传到github
如果还没配置过,需要配置账号信息 git config --global user.name "baymax-collab" git config --global user.email "baymax-collabtest.com"创建一个新的存储库 git clone gitgithub.com:xxxx cd test git switch --create main touch READ…...

前端javascript的DOM对象操作技巧,全场景解析
✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 所属的专栏:前端泛海 景天的主页:景天科技苑 文章目录 1.js的DOM介绍2.节点元素层级关系3.通过js修改,清空节点…...

TCP包头、TCP为什么安全可靠、UDP和TCP的区别、http协议
我要成为嵌入式高手之3月8日Linux高编第十八天!! __________________________________________________ 学习笔记 TPC包头 1、序号 发送端发送数据包的编号 2、确认号 已经确认接收到的数据的编号,只有当ACK为1时,该位才有用 …...

Android使用WebView打开内嵌H5网页
Android打开外部网页链接请参考上一篇文章 https://public.blog.csdn.net/article/details/136384559 继上篇,新建assets文章夹,将H5的网页资源放到此文件夹下 把H5的资源文件都拷进来 这个时候,将添加打开本地网页的代码: //打…...

UDP实现文件的发送、UDP实现全双工的聊天、TCP通信协议
我要成为嵌入式高手之3月7日Linux高编第十七天!! ———————————————————————————— 回顾 重要程序 1、UDP实现文件的发送 发端: #include "head.h"int main(void) {int sockfd 0;struct sockaddr_i…...

Yocto - Project Quick Build
欢迎光临! 这篇简短的文档将向您介绍使用 Yocto 项目构建典型镜像的过程。本文还介绍了如何为特定硬件配置构建。您将使用 Yocto Project 构建一个名为 Poky 的参考嵌入式操作系统。 Welcome! This short document steps you through the process for a typical i…...

深入探讨C++中的可变参数列表(Variadic Templates)
文章目录 导言可变参数列表的基本用法使用std::initializer_list应用场景 导言 在C编程中,处理可变数量参数的能力是一种非常有用的功能。通过可变参数列表,你可以编写更加通用和灵活的函数,从而提高代码的可读性和重用性。本文将详细介绍C中…...

MS2548 国产自动方向控制、半双工 RS-485 收发器 替代MAX13487
MS2548 国产自动方向控制、半双工 RS-485 收发器 替代MAX13487 北京冠宇铭通科技有限公司 肖小姐 产品简述 MS2548 是一个 5V 供电、半双工 RS-485 收发器。 芯片具有自动换向控制功能,可用于隔离485 端口,驱动器输入与使能信号一起配合控制芯片的状态&…...

数据库大师之路:Oracle在线学习平台全指南!
介绍数据库是由甲骨文公司开发的一款关系数据库管理系统(RDBMS),在数据库领域具有领先地位,并且以其系统可移植性而闻名。以下是对Oracle数据库的详细介绍: 市场地位:Oracle数据库是目前世界上流行的关系数…...

如何在Windows系统部署Jellyfin Server并实现公网访问内网影音文件
文章目录 1. 前言2. Jellyfin服务网站搭建2.1. Jellyfin下载和安装2.2. Jellyfin网页测试 3.本地网页发布3.1 cpolar的安装和注册3.2 Cpolar云端设置3.3 Cpolar本地设置 4.公网访问测试5. 结语 1. 前言 随着移动智能设备的普及,各种各样的使用需求也被开发出来&…...

华为北向网管NCE开发教程(3)CORBA协议开发
华为北向网管NCE开发教程(1)闭坑选接口协议 华为北向网管NCE开发教程(2)REST接口开发 华为北向网管NCE开发教程(3)CORBA协议开发 如果你真的还有选择的余地,能用REST,尽量用REST&…...

【算法训练营】最长公共子序列,倒水问题,奶牛吃草(Python实现)
最长公共子序列 时间限制:1 sec 空间限制:256 MB 问题描述 给定两个 1 到 n 的排列 A,B (即长度为 n 的序列,其中 [1,n] 之间的所有数都出现了恰好一次)。 求它们的最长公共子序列长度。 输入格式 第一行一个整数 n &a…...

Armadillo:矩阵类、向量类、Cube类和泛型类
文章目录 矩阵类、向量类、Cube类和泛型类Mat<type>matcx_matCol<type>veccx_vecRow<type>rowveccx_rowvecCube<type>cubecx_cubefield<object_type>SpMat<type>sp_matsp_cx_mat运算符: − * % / ! < > <…...

【守护健康】小脑萎缩患者必备营养指南
当生活给予我们挑战,我们选择用科学和关爱予以回应。面对小脑萎缩这一难题,正确的营养补充不仅是一剂强心针,更是患者康复之路上的坚实伙伴。今天,让我们一起了解那些能够助力小脑萎缩患者的神奇维生素! 1. 维生素B群…...

lvs集群中NAT模式
群集的含义 由多台主机构成,但对外表现为一个整体,只提供一个访问入口,相当于一台大型的计算机。 横向发展:放更多的服务器,有调度分配的问题。 垂直发展:升级单机的硬件设备,提高单个服务器自身功能。 …...

FPGA——三速自适应以太网设计(2)GMII与RGMII接口
FPGA——以太网设计(2)GMII与RGMII 基础知识(1)GMII(2)RGMII(3)IDDR GMII设计转RGMII接口跨时钟传输模块 基础知识 (1)GMII GMII:发送端时钟由MAC端提供 下…...

【校园导航小程序】2.0版本 静态/云开发项目 升级日志
演示视频 【校园导航小程序】2.0版本 静态/云开发项目 演示 首页 重做了首页,界面更加高效和美观 校园指南页 新增了 “校园指南” 功能,可以搜索和浏览校园生活指南 地图页 ①弃用路线规划插件,改用SDK开发包。可以无阻通过审核并发布…...

深入揭秘Lucene:全面解析其原理与应用场景(二)
本系列文章简介: 本系列文章将深入揭秘Lucene,全面解析其原理与应用场景。我们将从Lucene的基本概念和核心组件开始,逐步介绍Lucene的索引原理、搜索算法以及性能优化策略。通过阅读本文,读者将会对Lucene的工作原理有更深入的了解…...