list模拟
之前模拟了string,vector,再到现在的list,list的迭代器封装最让我影响深刻。本次模拟的list是双向带头节点的循环链表,该结构虽然看起来比较复杂,但是却非常有利于我们做删除节点的操作,结构图如下。

由于其节点结构特点,使得头插头删,尾插尾删的时间复杂度为O(1),因为我们只需要改变两边节点的指针链接即可。
节点类
先来个开胃小菜,了解了解节点类,该类比较简单。由于每个节点既要存储数据,又要存储前后节点指针,所以我们封装一个结构体,用的struct而不是class, 因为我们在后面的函数需要访问节点存储的节点指针成员来达到遍历的效果,所以用的是struct。至于封装性,别人也不敢直接访问我节点存的指针变量,举个例子,如果他拿到了我的节点指针ptr,访问成员用ptr->+(成员名字),问题就在成员名字,每个人实现的类名函数名那都是五花八门,这就导致这段访问成员的代码就不具有平台移值性。
template <class T> struct list_node{typedef list_node<T> node; 这里记忆一下node是节点类型名的重命名,后面还有很多重命名,免得搞混了。list_node(const T& val=T())T()是一个匿名对象,当我们未传对象初始化时,可用一个匿名对象来初始化value:_next(nullptr), _prev(nullptr) 用初始化列表来初始化,因为_val存的若是自定义类型, _val(val) 想要调用非默认构造函数得用初始化列表。 {;}node* _next;node* _prev;T _val;};
链表正向迭代器类:
不知道还记不记得string和vector的迭代器,string的iterator就是char*的重命名,而vector则是T*的重命名,那为什么list的迭代器不能把node*直接typedef呢,然后begin()函数返回哨兵位的下一个节点地址,end()函数返回哨兵位节点地址。假设我们就这样实现list的迭代器,然后结合下面的使用场景。
my_list::list<int>::iterator it = l1.begin();
while (it != l1.end())
{it++;
}
大家有没有想过it++到哪去了呢?是下一个节点吗?当然不是啦,链表的每个节点都是孤立的,你++怎么能到下一个节点呢?当时我立马就想到运算符重载了,在重载函数内部++指针是往后走,还是往前走,又或者是++到指针变成下一个节点地址那不都是我们实现者说了算。冷静,冷静,运算符重载首要原则就是操作数应该是自定义类型,所有类型的指针都是内置类型,所以节点指针它就没办法重载运算符,所以我们要把节点指针封装成类,这样我们才可以对这个类进行运算符重载,使得it++调用类内重载函数,到下一个节点。
template<class T,class Ref,class Ptr> Ref和Ptr这两个模板参数是为了实现const迭代器struct _list_iterator 使其成员函数可在类外访问{typedef list_node<T> node;typedef _list_iterator<T,Ref,Ptr> Self;_list_iterator(node* pnode=nullptr)//用一个node指针初始化其成员:_node(pnode){;}//无析构函数,迭代器不能释放节点_list_iterator(const Self& l)//临时对象的引用,例如接受begin():_node(l._node){;}Self& operator++()//this指针是个迭代器类型的指针{_node = _node->_next;return (*this);}Self operator++(int)首先这两个++运算符重载还构成函数重载编译默认给前置++多传一个参数,使其调用这个函数,如果你把这两个函数参数换一下,前置++去掉int,后置++加上int你就会发现前置++会去调用后置++的函数,因为编译器还是用参数匹配或者函数名修饰规则来找函数。{Self tmp = (*this);_node = _node->_next;return (tmp);}Self& operator--(){_node = _node->_prev;return *this;}Self operator--(int){Self tmp = (*this);_node = _node->_prev;return tmp;}
bool operator!=(const Self& l)//要加const,因为外部可能是与end()比较{return (_node != l._node);}bool operator==(const Self& l){return (_node == l._node);//判断迭代器内部的node指针是否相等}因为普通迭代器就一两个成员函数的返回值不同,比较简单的方法是用模板参数Ref控制返回const T&还是返回T&,Ref operator*(){return (_node->_val);//返回节点数据}//用类封装节点指针,使其可以重载++,*等运算符node* _node;};
我们实现的const迭代器并不是直接对迭代器类加个const,而是对特定的函数返回值做const修饰,所以迭代器成员函数无需加const.
->重载针对的是节点数据为自定义类型A,A类型存着一个变量_val,的时候,比如有一个类实例化对象为it, 我们希望重载->,使得下面的能调用到自定义类型内的变量,it->_val,但是it->返回的只是节点存的整个结构体A的地址,我们应该用it->->_val才访问到_val,而且这个重载也只能返回
地址,如果返回的是一个结构体对象,我们就要it->.(这有个点操作符)_val才能访问,更加奇怪,
但是当我们去测试的时候发现我们仅仅用it->_val即可访问,实际上是编译器会帮我们多加一个->,为了保证重载运算符的可读性,只能这样改了。还有如果节点存的数据成员是内置类型,我想应该是不可以用->重载的,你想返回的是int*, 但->必须是指向类的。
Ptr operator->()const第三模板参数原因,控制返回const T*或T*,数据不可修改{return &operator*(); }
反向迭代器类
针对list链表,我们可以直接定义一个反向迭代器类叫reverse_iterator,反向迭代器中的成员函数只有++,--和正向迭代器不一样,所以看完正向迭代器的成员函数也就能理解反向迭代器的成员 *解引用重载都是返回节点数据,->重载都是调用operator*()重载再取地址。
namespace my_list
{template<class iterator, class Ref, class Ptr>class reverse_iterator{public:typedef reverse_iterator<iterator, Ref, Ptr> Self;reverse_iterator(iterator it):_it(it)显示调用正向迭代器的拷贝构造{;}reverse_iterator(const Self& l):_it(l._it){;}Self& operator++() 复用正向迭代器_it的--函数{_it--;return (*this);}Self operator++(int){Self tmp = (*this);_it--;return (tmp);}Self& operator--() 复用正向迭代器_it的++函数{_it++;return *this;}Self operator--(int){Self tmp = (*this);_it++;return tmp;}bool operator!=(const Self& l){return (_it != l._it); 此处调用的是正向迭代器内部的比较}bool operator==(const Self& l){return (_it == l._it);}Ref operator*(){iterator tmp = _it;return *(--tmp);}Ptr operator->(){return &operator*();}private:iterator _it;};
}
链表类
我们知道目前设计的链表节点会保存前后位置的指针,这意味着链表类只需要管理头节点指针即可管理所有链表节点,有哨兵位则保存哨兵位地址,没有则保存第一个节点的地址。
template<class T>class list{typedef list_node<T> node;//设为私有,不让外部通过该处访问public:typedef _list_iterator<T, T&, T*> iterator;//设为公有typedef _list_iterator<T, const T&, const T*> const_iterator;调用const迭代器就传const T&和const T*控制函数返回值不被修改就达到const迭代器作用了下面这两个迭代器的typedef顺序不能改,
不然可能会将reverse_iterator<const_iterator, const T&, const T*>中的reverse_iterator识别为已经typedef的reverse_iterator<iterator, T&, T*>typedef reverse_iterator<const_iterator, const T&, const T*> const_reverse_iterator;typedef reverse_iterator<iterator, T&, T*> reverse_iterator;reverse_iterator rbegin(){return ((reverse_iterator)end());}const_reverse_iterator rbegin() const{return (const_reverse_iterator)end();}reverse_iterator rend(){return (reverse_iterator)begin();}const_reverse_iterator rend() const{return (const_reverse_iterator)begin();}iterator begin(){return _phead->_next;//返回匿名对象}iterator end(){return _phead;}const_iterator begin()const{return _phead->_next;//返回匿名对象}const_iterator end()const 要加const,要让const list<T>调用该函数,返回const迭代器{return _phead;}void Createhead(){_phead = new node;_phead->_next = _phead;_phead->_prev = _phead;}list(){Createhead();}list(const list<T>& l)//list的拷贝构造{Createhead();for (auto& c : l){push_back(c);}}list(size_t n, const T& value = T()){Createhead();while (n--){push_back(value);}}list(int n, const T& value = T()){Createhead();while (n--){push_back(value);}}template<class inputiterator>list(inputiterator first, inputiterator last) 用一段迭代器区间初始化{Createhead();while (first != last){push_back(*first);++first;}}void swap(list<int>& lt){std::swap(_phead, lt._phead);}list<T>& operator=(list<int> lt)//list<int>可用list代替{swap(lt);return *this;}~list(){clear();delete _phead;_phead = nullptr;}void clear(){iterator it = begin();while (it != end()){it = erase(it);}}void push_front(const T& value = T()){insert(begin(), value);}void push_back(const T& value = T()){//node* newnode = new node(value);//node* tail = _phead->_prev;链接新节点,注意链接关系//tail->_next = newnode;//newnode->_prev = tail;//newnode->_next = _phead;//_phead->_prev = newnode;insert(end(), value);}iterator insert(iterator pos, const T& value = T()){node* newnode = new node(value);node* pose = pos._node;node* cur = pose->_prev;cur->_next = newnode;newnode->_prev = cur;newnode->_next = pose;pose->_prev = newnode;return newnode;}void pop_back(){erase(--end());}void pop_front(){erase(begin());}size_t size()const{int n = 0;iterator it = begin();while (it != end()){it++;n++;}return n;}bool empty()const{return size() == 0;}iterator erase(iterator pos){node* pose = pos._node;//迭代器it是一个类,访问成员变量用.操作符pos._node = pos._node->_next;node* cur = pose->_prev;cur->_next = pose->_next;pose->_next->_prev = cur;delete pose;return pos;}private:node* _phead;};
}
上述我们封装了四个类,虽然一下子不知道如何说清楚为什么要分开描述,如果都挤在一起,那肯定是有点冗余,类本质是用来描述的,个人认为一种类应该是只描述一种对象的,如果一个类既描述链表节点,又描述链表,我感觉对于我后续的实现会很麻烦。
相关文章:
list模拟
之前模拟了string,vector,再到现在的list,list的迭代器封装最让我影响深刻。本次模拟的list是双向带头节点的循环链表,该结构虽然看起来比较复杂,但是却非常有利于我们做删除节点的操作,结构图如下。 由于其节点结构特…...
python字典:怎么取出key对应的值
目录 python中的字典是什么 怎么判断key是否在字典中 怎么取出key对应的值 总结 python中的字典是什么 在Python中,字典(Dictionary)是一种无序且可变的数据类型,用于存储键-值(Key-Value)对。字典通过…...
okvis
论文 Keyframe-Based Visual-Inertial SLAM Using Nonlinear Optimization 摘要 由于两种感知模式的互补性,视觉和惯性线索的融合在机器人中变得很流行。虽然迄今为止大多数融合策略都依赖于过滤方案,但视觉机器人界最近转向了非线性优化方法&#x…...
fabric js双击弹出菜单, 双击弹出输入框 修改文字 群组对象
<!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>测试1</title><style type"text/css">body {background-color: #ccc;float: left;}#main {background-color: #fff;border: 1px…...
路由器工作原理
路由器原理 路由概述 路由:跨越从源主机到目标主机的一个互联网络来转发数据包的过程。(为数据包选择路径的过程) 作用:路由器是连接不同网段的。 转发依据: 路由表:路径选择全看路由表,根…...
在centos 7系统docker上构建mysql 5.7
一、VM上已经安装centos 7.9,且已完成docker的构建 二、安装mysql5.7 安装镜像:[rootlocalhost lll]# docker pull mysql:5.7 查看镜像[rootlocalhost lll]# docker images 根据镜像id构建mysql容器,且分配端口号[rootlocalhost lll]# dock…...
数据库的介绍和分类
目录 一、数据库的介绍和分类 二、命令行客户端 三、数据操作 四、查询的基本操作 五、条件查询 六、分组和聚合 资料获取方法 一、数据库的介绍和分类 数据库:长期存储在计算机内、有组织的数据集合 数据库的分类: 关系型数据库 以表格的形式…...
职责链模式——请求的链式处理
1、简介 1.1、概述 很多情况下,在一个软件系统中可以处理某个请求的对象不止一个。例如SCM系统中的采购单审批,主任、副董事长、董事长和董事会都可以处理采购单,他们可以构成一条处理采购单的链式结构。采购单沿着这条链进行传递ÿ…...
docker中涉及的挂载点总结
文章目录 1.场景描述2. 容器信息在主机上位置3. 通过docker run 命令4、通过Dockerfile创建挂载点5、容器共享卷(挂载点)6、最佳实践:数据容器 1.场景描述 在介绍VOLUME指令之前,我们来看下如下场景需求: 1ÿ…...
elasticsearch 官方优化建议
.一般建议 a.不要返回过大的结果集。这个建议对一般数据库都是适用的,如果要获取大量结果,可以使用search_after api,或者scroll (新版本中已经不推荐)。 b.避免大的文档。 2. 如何提高索引速度 a.使用批量请求。为了…...
Kubernetes(K8s)从入门到精通系列之五:K8s的基本概念和术语之应用类
Kubernetes K8s从入门到精通系列之五:K8s的基本概念和术语之应用类 一、Service与Pod二、Label与标签选择器三、Pod与Deployment四、Service的ClusterIP地址五、Service的外网访问问题六、有状态的应用集群七、批处理应用八、应用配置问题九、应用的运维一、Service与Pod Ser…...
DevOps(四)
CD(二) 1. CDStep 1 - 上传代码Step 2 - 下载代码Step 3 - 检查代码Step 4 - 编译代码Step 5 - 上传仓库Step 6 - 下载软件Step 7 - 制作镜像Step 8 - 上传镜像Step 9 - 部署服务2. 整体预览2.1 预览1. 修改代码2. 查看sonarqube检查结果3. 查看nexus仓库4. 查看harbor仓库5.…...
Element-plus侧边栏踩坑
问题描述 el-menu直接嵌套el-menu-item菜单,折叠时不会出现文字显示和小箭头无法隐藏的问题,但是实际开发需求中难免需要把el-menu-item封装为组件 解决 vue3项目中嵌套两层template <template><template v-for"item in list" :k…...
支持多种通信方式和协议方便接入第三方服务器或云平台
2路RS485串口是一种常用的通信接口,可以支持Modbus Slave协议,并可接入SCADA、HMI、DSC、PLC等上位机。它还支持Modbus RTU Master协议,可用于扩展多达48个Modbus Slave设备,如Modbus RTU远程数据采集模块、电表、水表、柴油发电机…...
使用 OpenCV 进行图像模糊度检测(拉普拉斯方差方法)
写在前面 工作中遇到,简单整理人脸识别中,对于模糊程度较高的图像数据,识别率低,错误率高。虽然使用 AdaFace 模型,对低质量人脸表现尤为突出。但是还是需要对 模糊程度高的图像进行丢弃处理当前通过阈值分类ÿ…...
神经网络简单介绍
人工神经网络(artififial neural network) 简称神经网络,它是一种模仿生物神经网络结构和功能的非线性数学模型。 神经网络通过输入层接受原始特征信息,再通过隐藏层进行特征信息的加工和提取,最后通过输出层输出结果。 根据需要神经网络可以…...
16位S912ZVML32F3MKH、S912ZVML31F1WKF、S912ZVML31F1MKH混合信号MCU,适用于汽车和工业电机控制应用。
S12 MagniV微控制器是易于使用且高度集成的混合信号MCU,非常适合用于汽车和工业应用。S12 MagniV MCU提供单芯片解决方案,是基于成熟的S12技术的完整系统级封装 (SiP) 解决方案,在整个产品组合内软件和工具都兼容。 S12 MagniV系统级封装 (S…...
力扣 509. 斐波那契数
题目来源:https://leetcode.cn/problems/fibonacci-number/description/ C题解1:根据题意,直接用递归函数。 class Solution { public:int fib(int n) {if(n 0) return 0;else if(n 1) return 1;else return(fib(n-1) fib(n-2));} }; C题…...
使用 DolphinDB TopN 函数探索高效的Alpha因子
DolphinDB 已经有非常多的窗口计算函数,例如 m 系列的滑动窗口计算,cum 系列累计窗口计算,tm 系列的的时间窗口滑动计算。但是所有这类函数都是对窗口内的所有记录进行指标计算,难免包含很多噪音。 DolphinDB 的金融领域用户反馈…...
超聚变和厦门大学助力兴业银行构建智慧金融隐私计算平台,助力信用卡业务精准营销...
兴业银行与超聚变数字技术有限公司、厦门大学携手,发挥产学研用一体化整体优势联合建设,厦门大学提供先进的算法模型及科研能力,超聚变提供产品解决方案及工程能力,兴业银行提供金融实践能力,三方发挥各自领域优势&…...
AI赋能开发:让快马平台智能理解并生成产区标准图交互应用
AI赋能开发:让快马平台智能理解并生成产区标准图交互应用 最近在做一个农产品产区标准查询系统的项目,发现用传统方式开发这类需求特别费时。比如要处理用户自然语言查询、动态生成地图、实现智能推荐逻辑,光写基础代码就得花好几天。后来尝…...
龙虾agent-browser获得chromium包问题
小龙虾非常火爆,在装agent-browser的时候,普通人往往被chromium的安装堵死了。网上的跨域安装方法一大堆,包括用镜像站点,国内所有的镜像站点都不行。但是真正能走通的,我到最后也没有试出来。最后只能自己想出一种手动…...
从代码到部署:手把手复现CenterPoint(PyTorch版)在KITTI数据集上的完整流程
从零实现CenterPoint:KITTI数据集3D目标检测全流程实战指南 为什么选择CenterPoint进行3D目标检测? 在自动驾驶和机器人感知领域,3D目标检测一直是核心技术难题。传统基于锚框(Anchor-based)的方法在处理旋转物体时表现…...
DanKoe 视频笔记:人生经验课:给18岁自己的信
在本节课中,我们将学习一位28岁人士回顾过去,总结出的核心人生经验。这些经验旨在帮助年轻人,特别是那些感到迷茫、渴望超越平凡生活的人,建立自主性、明确目标并采取有效行动。我们将把这些经验整理成一套清晰的教程,…...
用VSCode+PlatformIO给ESP32做个简易手表:基于LVGL和1.3寸屏的UI实战
基于LVGL的ESP32智能手表开发实战:从硬件驱动到UI设计全流程 在创客圈里,ESP32凭借其出色的性价比和丰富的功能接口,一直是物联网项目的热门选择。而当我们把目光投向更直观的人机交互领域时,LVGL(Light and Versatile…...
AI读脸术备份恢复指南:手把手教你搭建高可用人脸识别服务
AI读脸术备份恢复指南:手把手教你搭建高可用人脸识别服务 1. 项目背景与需求分析 人脸识别技术已经成为现代数字服务的重要组成部分,从电商个性化推荐到智能安防系统,都依赖这项技术的稳定运行。AI读脸术镜像基于OpenCV DNN深度神经网络构建…...
GLM-4.1V-9B-Base部署教程:NVIDIA驱动版本兼容性验证与降级方案
GLM-4.1V-9B-Base部署教程:NVIDIA驱动版本兼容性验证与降级方案 1. 模型概述 GLM-4.1V-9B-Base是智谱开源的一款视觉多模态理解模型,专注于图像内容识别与分析任务。该模型具备以下核心能力: 图片内容描述与场景理解图像主体识别与定位颜色…...
敏捷团队沟通技巧:减少冲突的5个方法
在敏捷开发环境中,软件测试从业者常面临跨职能冲突的挑战。数据显示,超过70%的项目延迟源于沟通不畅,尤其在测试与开发团队之间,角色目标错位(如开发侧重快速交付,测试聚焦风险防控)易引发摩擦。…...
STEP3-VL-10B实际作品集:MMBench 92.05分视觉识别能力高清图文输出示例
STEP3-VL-10B实际作品集:MMBench 92.05分视觉识别能力高清图文输出示例 1. 引言:当AI“看懂”了世界 你有没有想过,让AI像人一样“看懂”一张图片,到底有多难? 这不仅仅是识别出图片里有什么东西那么简单。比如给你…...
STM32F407实战:基于CubeMX与FreeRTOS的SDIO-FatFs文件系统高效读写方案
1. 环境准备与CubeMX基础配置 第一次接触STM32F407的SD卡存储时,我被各种专业术语搞得晕头转向。后来发现,只要用对工具和方法,实现文件系统读写其实没那么复杂。CubeMX这个图形化配置工具真是开发者的福音,它能帮我们自动生成80%…...
