(unordered)map和set封装(底层红黑树)

map和set封装

文章目录

  • map和set封装
    • 设计问题(知其所以然)
      • 为什么要对iterator进行封装?
      • 为什么要引入Self Ref Ptr这些模板参数?
      • 为什么是试图从non_const转变为const,而不是const转为non_const
        • 如何解决
      • 为什么说能加const把const加上
      • 增加构造函数解决set调用红黑树实现插入时iterator的转换
        • 解决方法
  • unordered_map/set封装
      • 为什么迭代器中指向哈希表的指针要加const?为什么可以加const?
        • 为什么指针要加const
        • 为什么可以加const,不会影响对哈希表的增加删除吗?
      • 老生常谈的问题
      • 两个由const引发的bug

设计问题(知其所以然)

为什么要对iterator进行封装?

是否需要分装和指向的类型有关:

  • 如果迭代器指向的是内置类型,不需要分装
  • 如果是自定义类型,则需要重载一系列操作符,因此需要分装

在STL的实现中,string的实现就没有用到封装,是因为string的迭代器为char* ,进行*(解引用),++等操作时不需要重载

为什么要引入Self Ref Ptr这些模板参数?

这里和list的底层实现道理一致

因为迭代器有const 和non_const两种,而不同的迭代器种类要有不同的返回值类型,也就是说如果我们不传入模板参数,很多函数要写两次(返回值类型不同),造成代码的冗余

template<class T>class list{.......public:typedef list_iterator<T, T&, T*> iterator;typedef list_iterator<T, const T&, const T*> const_iterator;typedef Reverse_Iterator<iterator, T&, T*> reverse_iterator;typedef Reverse_Iterator<const_iterator,const T&,const T*> const_reverse_iterator;}

下面的注释可以帮助理解如何解决冗余:

	template<class T,class Ref,class Ptr>class list_iterator{typedef listNode<T> Node;public:Node* _pnode;list_iterator(Node* pnode):_pnode(pnode){}//const T& 或 T& Ref operator*(){return _pnode->_val;//->结构体指针访问结构体成员变量的方式}//const T* 或 T*Ptr operator->(){return &(_pnode->_val);}typedef list_iterator<T, Ref, Ptr> Self;//iterator 或 const_iteratorSelf& operator++(){_pnode = _pnode->_next;return *this;}.......}

为什么是试图从non_const转变为const,而不是const转为non_const

	class Set{private:struct setKeyofT{...}RBTree<K, K, setKeyofT> _t;public://底层是对树的封装typedef typename RBTree<K, K, setKeyofT>::const_iterator iterator;//两个迭代器都是const迭代器typedef typename RBTree<K, K, setKeyofT>::const_iterator const_iterator;iterator begin(){return _t.begin();}const_iterator begin()const{return _t.begin();}iterator end(){return _t.end();//此行报错}const_iterator end()const{return _t.end();}
	s.Insert(3);s.Insert(1);s.Insert(6);s.Insert(5);s.Insert(9);s.Insert(4);auto it = s.begin();while (it != s.end()){cout << *it << " ";++it;}cout << endl;

报错原因,无法转变树中的迭代器,需要自己实现转变:

在这里插入图片描述

为什么是试图从non_const转变为const:

  • 此时调用的是第一个begin在这里插入图片描述

  • 返回的是树的iterator,但是set的iterator相当于树的const_iterator,类型不匹配而且不能自动发生转换,因此报错

如何解决

set在STL中源码是这样解决的:

在这里插入图片描述

这样写非常的巧妙,大佬不愧是大佬:

这样传进来的_t就有const修饰,调用的是RBTree中const_iterator begin(),const可以满足和set中(const)iterator的配对

解决了上述转化的问题

此时只需要写一个就可以满足要求,多了是重复的,会报错

		typedef typename RBTree<K, K, setKeyofT>::const_iterator iterator;typedef typename RBTree<K, K, setKeyofT>::const_iterator const_iterator;iterator begin()const{return _t.begin();}/*const_iterator begin()const{return _t.begin();}*/

在这里插入图片描述

为什么说能加const把const加上

因为权限可以缩小,普通对象是可以调用的,但如果不加const,const对象就无法调用

增加构造函数解决set调用红黑树实现插入时iterator的转换

template<class K>
class Set
{RBTree<K, K, setKeyofT> _t;
public:typedef typename RBTree<K, K, setKeyofT>::const_iterator iterator;typedef typename RBTree<K, K, setKeyofT>::const_iterator const_iterator;pair<iterator, bool> Insert(const K& k){return _t.Insert(k);}
};

在这里插入图片描述

map可以正常调用而set不行,因为_t调用的红黑树的insert返回的是普通itereator,但是set的iterator实际上是const_iterator,所以报错。要想办法转换

解决方法

构造一个转换的函数拿iterator构造成const_iterator

大佬还是大佬,再来膜拜一下大佬的思路

struct _TreeIterator
{.....typedef _TreeIterator<T,Ref,Ptr> Self;//迭代器本身,受Ref和Ptr影响typedef _TreeIterator<T, T&, T*> iterator;//始终是普通迭代器,在 _TreeIterator中封装了一个普通迭代器(的类型)_TreeIterator(const iterator& it)//支持传入普通迭代器调用构造函数:_node(it._node){}}
  • 当这个迭代器被实例化为const迭代器,这是一个构造函数
  • 当实例化为普通迭代器,这是一个拷贝构造
    在这里插入图片描述

unordered_map/set封装

为什么迭代器中指向哈希表的指针要加const?为什么可以加const?

为什么指针要加const

HashTable中,const对象调用迭代器,传入的指针是const类型,会造成权限的放大

const_iterator end()const
{return const_iterator(nullptr, this);//因为是const,所以this是const
}

在这里插入图片描述

为什么可以加const,不会影响对哈希表的增加删除吗?

迭代器里不用修改哈希表,哈希表的修改是基于Node改变,Node不为const

老生常谈的问题

为什么要在迭代器中增加不受传值影响的iterator迭代器类型(不是对象)

因为set的两个迭代器都是const迭代器,但返回的时候是哈希表中的普通迭代器,因为是自定义类型,需要添加构造函数实现转换(同map和set)

两个由const引发的bug

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

  • 图一:加上const以后,323行调用的end函数为const对象调用的,返回对象为cosnt_iterator,const_iterator不能转换为iterator
    在这里插入图片描述

  • 图二:多加了一个const,在初始化的时候出现错误(const const T*)
    在这里插入图片描述

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

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

相关文章

【Java 进阶篇】JDBC PreparedStatement 详解

在Java中&#xff0c;与关系型数据库进行交互是非常常见的任务之一。JDBC&#xff08;Java Database Connectivity&#xff09;是Java平台的一个标准API&#xff0c;用于连接和操作各种关系型数据库。其中&#xff0c;PreparedStatement 是 JDBC 中一个重要的接口&#xff0c;用…

FileZila 实现wind10与Linux系统文件互传

【FileZila】实现windows与Linux系统文件互传

GD32F103x IIC通信

1. IIC通信 1.IIC的介绍 IIC总线有两条串行线&#xff0c;其一是时钟线SCK&#xff08;同步&#xff09;&#xff0c;其二是数据线SDA。只有一条数据线属于半双工。应用中&#xff0c;单片机常常作为主机&#xff0c;外围器件可以挂载多个。&#xff08;当然主机也可以有多个。…

想要精通算法和SQL的成长之路 - 验证二叉树的前序序列化

想要精通算法和SQL的成长之路 - 验证二叉树的前序序列化 前言一. 验证二叉树的前序序列化 前言 想要精通算法和SQL的成长之路 - 系列导航 一. 验证二叉树的前序序列化 原题链接 思路&#xff08;参考负雪明图&#xff09;&#xff1a; 首先我们看题目所给的字符串&#xff…

【Diffusion】DDPM - (2)公式推导 之 前向扩散

1、加噪过程 1、将 图像 x 0 x_0 x0​ 像素值映射到 [-1, 1] 之间 x 255 2 − 1 , w h e r e    x 为图像中的像素值 \quad \frac{x}{255} \times 2-1, \quad where \; x 为图像中的像素值 255x​2−1,wherex为图像中的像素值 \quad 2、生成一张尺寸相同的噪声图片,像…

边缘计算网关

一、项目整体框架图 二、项目整体描述 边缘计算网关项目主要实现了智能家居场景和工业物联网场景下设备的数据采集和控制。 整个项目分为三大层&#xff1a;用户接口层、网关层、设备层。 其中用户层通过QT客户端、WEB界面及阿里云提供数据展示和用户接口。 网关使用虚拟机代替…

Django之十二:模板的继承+用户列表

模板的继承 新建layout.html&#xff1a; {% load static %} <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Title</title><link rel"stylesheet" href"{% static plugins…

231003-四步MacOS-iPadOS设置无线竖屏随航SideCar

Step 0&#xff1a;MacOS到iPad无线竖屏随航显示&#xff0c;最终效果 Step 1&#xff1a; 下载 Better Display Step 2&#xff1a;在设置中新建虚拟屏幕&#xff0c;创建虚拟屏幕 Step 3&#xff1a;进行如下设置 Step 4&#xff1a;注意事项 ⚠️ 设置后的虚拟屏幕与Sideca…

nodejs+vue晓海网上订餐系统elementui

管理员功能需求 管理员登陆后&#xff0c;主要模块包括首页、个人中心、用户管理、菜单信息管理等功能。 第三章 系统分析 10 3.1需求分析 10 3.2可行性分析 10 3.2.1技术可行性&#xff1a;技术背景 10 3.2.2经济可行性 11 3.2.3操作可行性&#xff1a; 11 3.3性能分析 11 3.4…

IntelliJ IDEA配置Cplex12.6.3详细步骤

Cplex12.6.3版IntelliJ IDEA配置详细步骤 一、Cplex12.6.3版下载地址二、Cplex安装步骤三、IDEA配置CPLEX3.1 添加CPLEX安装目录的cplex.jar包到项目文件中3.2 将CPLEX的x64_win64文件夹添加到IDEA的VM options中 四、检查IDEA中Cplex是否安装成功卸载Cplex 一、Cplex12.6.3版下…

我用PYQT5做的第一个实用的上位机项目(三)

基本的程序框架&#xff1a; 因为自己不是专业的程序员&#xff0c;只是一个搞电气控制的“票友”&#xff0c;所以尽量减少手动输入 代码量&#xff0c;能在Qt Dsigner里面完成的组态就不要放在代码里面完成。 在框架的建设方面&#xff0c;尽量做到集中和整合&#xff0c;位…

科技+智慧+颜值,智慧公厕黑科技提升城市形象

现代社会的科技和智慧正以惊人的速度渗透到我们生活的各个方面&#xff0c;包括公共设施。而作为城市形象的重要组成部分之一&#xff0c;公厕也在不断创新中迎来了智慧的时代。 在传统的公厕中&#xff0c;一些问题一直困扰着我们&#xff0c;比如厕位的占用情况、空气质量的…

Git多账号管理通过ssh 公钥的方式,git,gitlab,gitee

按照目前国内访问git&#xff0c;如果不科学上网&#xff0c;我们很大可能访问会超时。基于这个&#xff0c;所以我现在的git 配置已经增加到了3个了 一个公司gitlab&#xff0c;一个git&#xff0c;一个gitee. 以下基于这个环境&#xff0c;我们来说明下如何创建配置ssh公钥。…

UE中制作棋盘格材质效果

在UE中通过这个小技巧制作棋盘格材质效果&#xff0c;可以快速预览UV拉伸情况&#xff0c;方便调试导入的模型。 1.操作步骤 1.1 首先新建材质&#xff0c;Shading Model&#xff08;着色模式&#xff09;设置为Unlit&#xff08;无光照&#xff09;&#xff1a; 1.2 我们…

前端相关题目随笔

Vh虽然获取到了视口高度&#xff0c;但是vh会随着屏幕的、大小变化&#xff0c;所以当减去一个数字之后&#xff0c;就会显示错误。 生成id 如果没有设置id&#xff0c;则可以通过new Date.getTime()获取一个时间&#xff0c;作为一个单独的id&#xff0c;也可以通过下载uuid生…

JavaScript系列从入门到精通系列第十二篇:JavaScript中对象的简介和对象的基本操作以及JavaScript中的属性值和属性名

文章目录 前言 一&#xff1a;对象分类 1&#xff1a;内建对象 2&#xff1a;宿主对象 3&#xff1a;自建对象 二&#xff1a;对象的基本操作 1&#xff1a;创建对象 2&#xff1a;向对象中添加属性 3&#xff1a;读取对象中的属性 4&#xff1a;修改对象中的属性 三…

机器学习7:逻辑回归

一、说明 逻辑回归模型是处理分类问题的最常见机器学习模型之一。二项式逻辑回归只是逻辑回归模型的一种类型。它指的是两个变量的分类&#xff0c;其中概率用于确定二元结果&#xff0c;因此“二项式”中的“bi”。结果为真或假 — 0 或 1。 二项式逻辑回归的一个例子是预测人…

【PickerView案例12-info_plist-PCH文件介绍 Objective-C语言】

一、给大家介绍一下我们项目的一些文件: 1.这个呢,是项目的基础文件: 一些类啊: 一些图片啊: 还有加载图片, 最主要,就是这个东西:info.plist:文件 info.plist: 2.那,需要大家了解一点,关于它的历史啊: 我们现在用的时候,都是从xcode6.4开始的, 或者说,直…

Python 数据分析与挖掘(一)

Python 数据分析与挖掘&#xff08;数据探索&#xff09; 数据探索 1.1 需要掌握的工具&#xff08;库&#xff09; 1.1.1 Nump库 Numpy 提供多维数组对象和各种派生对象&#xff08;类矩阵&#xff09;&#xff0c;利用应用程序接口可以实现大量且繁琐的数据运算。可以构建…

Linux 5种网络模型

[参考]&#xff1a;《黑马程序员Redis》https://www.bilibili.com/video/BV1cr4y1671t/?p166&share_sourcecopy_web&vd_source9e65300ccca322aeb367bb1eb677b0fc [参考]&#xff1a;《操作系统》 [参考]&#xff1a;《UNIX网络编程》 为了避免用户应用导致冲突甚至内…