(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.hqwc.cn/news/124066.html

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

相关文章

【C++】模板初阶 -- 详解

一、泛型编程 // 实现一个通用的交换函数&#xff1a; void Swap(int& left, int& right) {int temp left;left right;right temp; }void Swap(double& left, double& right) {double temp left;left right;right temp; }void Swap(char& left, ch…

PDF文件压缩软件 PDF Squeezer mac中文版​软件特点

PDF Squeezer mac是一款macOS平台上的PDF文件压缩软件&#xff0c;可以帮助用户快速地压缩PDF文件&#xff0c;从而减小文件大小&#xff0c;使其更容易共享、存储和传输。PDF Squeezer使用先进的压缩算法&#xff0c;可以在不影响文件质量的情况下减小文件大小。 PDF Squeezer…

使用Docker部署ElasticSearch7+ELK(附带ES操作操作命令集)

ElasticSearch 7ELK 程序安装Docker安装下载ES镜像提前创建挂载文件夹添加配置文件创建并启动容器可能出现的异常安装IK分词使用ElasticHD客户端工具(目前使用发现无法做增删改)安装Kibana 软件包安装安装ElasticSearch&#xff08;需要JDK1.8&#xff09;安装IK&#xff08;下…

(二)激光线扫描-相机标定

1. 何为相机标定? 当相机拍摄照片时,我们看到的图像通常与我们实际看到的不完全相同。这是由相机镜头引起的,而且发生的频率比我们想象的要高。 这种图像的改变就是我们所说的畸变。一般来说,畸变是指直线在图像中出现弯曲或弯曲。 这种畸变我们可以通过相机标定来进行解…

论坛项目知识回顾

目录 一. MySQL 建表 二. MyBatis 三. dao层 四. service层 五. controller层 六 component层 七. configuration层 八. utils 包 九. common 包 十. Exception 十一. interceptor包 十二. 代码有使用什么SpringBoot注解 一. MySQL 建表 首先使用 MySQL 进行表的创…

【iptables 实战】06 iptables网络防火墙实验

一、现状说明 在上一节中&#xff0c;我们将两个网段的机器&#xff0c;通过中间机器的网络转发&#xff0c;能达到互通。再来回顾一下这个网络连接的图 这一节&#xff0c;我们将通过设置机器B的iptables规则&#xff0c;来做一些防火墙实验 机器A模拟公网的一台服务器&#…

【生命周期】

生命周期 1 引出生命周期2 分析生命周期3 总结生命周期 1 引出生命周期 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta …

微服务技术栈-认识微服务和第一个微服务Demo

文章目录 前言一、认识微服务二、微服务技术栈三、Eureka注册中心四、微服务DEMO1、搭建eureka-server2、服务注册和服务发现 总结 前言 随着业务的不断复杂&#xff0c;对服务的要求也越来越高&#xff0c;服务架构也从单体架构逐渐演变为现在流行的微服务架构。 本章就从微服…

【JVM】垃圾回收(GC)详解

垃圾回收&#xff08;GC&#xff09;详解 一. 死亡对象的判断算法1. 引用计数算法2. 可达性分析算法 二. 垃圾回收算法1. 标记-清除算法2. 复制算法3. 标记-整理算法4. 分代算法 三. STW1. 为什么要 STW2. 什么情况下 STW 四. 垃圾收集器1. CMS收集器&#xff08;老年代收集器&…

算法-动态规划/trie树-单词拆分

算法-动态规划/trie树-单词拆分 1 题目概述 1.1 题目出处 https://leetcode.cn/problems/word-break/description/?envTypestudy-plan-v2&envIdtop-interview-150 1.2 题目描述 2 动态规划 2.1 解题思路 dp[i]表示[0, i)字符串可否构建那么dp[i]可构建的条件是&…

【计算机网络黑皮书】应用层

【事先声明】 这是对于中科大的计算机网络的网课的学习笔记&#xff0c;感谢郑烇老师的无偿分享 书籍是《计算机网络&#xff08;自顶向下方法 第6版&#xff09;》 需要的可以私信我&#xff0c;无偿分享&#xff0c;课程简介下也有 课程连接 目录 应用层网络应用的原理应用架…

Linux使用之xshell、xftp保姆教学(含安装包,详细使用方法,连接失败解决方法)

前言 链接: FTP&#xff0c;SSH服务器介绍 这是我之前写的一篇博客&#xff0c;其中介绍了Ubuntu操作系统的一些常用命令以及服务器介绍&#xff0c;这篇文章就向大家详细介绍如何安装及应用这些服务器&#xff0c;我以xshell、xftp为例。 安装包&#xff0c;使用方法&#xf…