【C++初阶】list的模拟实现 附源码

一.list介绍

list底层是一个双向带头循环链表,这个我们以前用C语言模拟实现过,->双向带头循环链表

下面是list的文档介绍: list文档介绍

我们会根据 list 的文档来模拟实现 list 的增删查改及其它接口。


 

二.list模拟实现思路

既然是用C++模拟实现的,那么一定要封装在类里

为了适合各种类型的数据,会使用模板

节点 Node

了解双向循环带头链表的都知道,我们需要一个节点 (Node),之前用C语言实现的时候,我们写了一个叫做 BuynewNode 的函数来获取节点,而在C++里我们用类封装一个,注意这个用 struct 封装比较好,因为 struct 默认是公有的,这样方便我们访问,所以可以写一个类:

    struct  list_node

迭代器  iterator

我们知道,C++提供了一种统一的方式来访问容器,这就是迭代器,string 和 vector 的迭代器模拟实现很简单,因为 string 和 vector 底层是用数组实现的,数组是一段连续的物理空间,支持随机访问,所以它是天然的迭代器

但是链表不一样,它不是一段连续的物理空间,不支持随机访问,所以想让 list 的迭代器在表面上和 string,vector 的迭代器用起来没有区别,我们在底层上就需要用类封装迭代器,然后再迭代器类的内部,重载  ++  --  *  ->  !=  ==  这些迭代器会用到的运算符

所以创建一个迭代器类:

   struct  list_iterator

const 迭代器  const_iterator

实现的普通的迭代器,还有 const 迭代器,const 迭代器的意思是让指针指向的内容不变,而指针本身可以改变,例如指针++,指针-- 这种操作,所以 const 迭代器与普通迭代器的不同只有 重载 * 运算符的返回值不同,它是 const  T&  (T是模板参数),重写一个const 迭代器类又显得太冗余,代码的可读性就降低了;

前面在学习模板时,我们知道不同的模板参数,编译器会生成不同的函数,所以我们选择加一个模板参数 :Ref 。这样只要在显示实例化模板参数时:

              普通迭代器就传 T&

              const 迭代器就传 const T&

-> 运算符重载

看下面这段代码:

using namespace std;struct A
{A(int a1,int a2):_a1(a1),_a2(a2){}int _a1;int _a2;
};void test_list()
{list<A> lt;   //实例化自定义类型lt.push_back(A(1, 1));lt.push_back(A(2, 2));lt.push_back(A(3, 3));lt.push_back(A(4, 4));list<A>::iterator it = lt.begin();while (it != lt.end()){cout << it->_a1 << " " << it->_a2 << endl;  //像指针一样访问自定义类型里的成员变量it++;}	
}int main()
{test_list();return 0;
}

有时候,实例化的模板参数是自定义类型,我们想要像指针一样访问访问自定义类型力的成员变量,这样显得更通俗易懂,所以就要重载 -> 运算符,它的返回值是 T* ,但是正常来说这里应该是这样访问的: it -> -> _a1

因为迭代器指向的是 整个自定义类型,要想再访问其内部成员应该再使用一次 -> (这个->就不是重载的 -> ,就是普通的 -> ),但是上面的代码为什么就写了一个 -> ,这个是C++语法把这里特殊化了。

那该怎么在迭代器类里重载 -> 运算符呢?

和const 迭代器一样,只需要再加一个模板参数 :Ptr

显示实例化的时候传 T* 就行了。 

迭代器类 模拟实现源码: struct list_iterator

以上的都算 list 模拟实现的难点,其他的像 重载 ++ 什么的,对于学过数据结构的小伙伴们是非常简单的,就不赘述了,没学过的可以看看这篇文章:双向带头循环链表

template<class T,class Ref,class Ptr>   //三个模板参数struct list_iterator   //封装迭代器{typedef list_node<T> Node;  //重命名节点typedef list_iterator<T, Ref, Ptr> self;  //重命名迭代器类型Node* _node;list_iterator(Node*node)   //构造函数,单参数的构造函数支持隐式类型转换:_node(node){}//重载 * ++ -- != == ->Ref operator*() const{return _node->_val;}Ptr operator->() const{return &_node->_val;}self& operator++()   //前置++{_node = _node->_next;return *this;}self operator++(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& lt) const{return _node != lt._node;}bool operator==(const self& lt) const{return _node == lt._node;}};

list

我们在用C语言实现双向带头循环链表时,会先初始化链表的头(head),即让它的 前驱指针(prev)和后继指针(next)都指向自己

在C++的模拟实现 list 中,我们会创建一个类 list  来管理链表的节点并实现增删查改及其它接口,所以 list  的构建函数就是初始化 头(head)节点


三.源码

list.h

 

我们可以模拟实现以上接口,具体函数的逻辑可以查阅文档,实现起来都是很简单的。

namespace nagi   //把模拟实现list的类都放在一个命名空间里封装起来
{template<class T>struct list_node   //创建节点{list_node<T>* _prev;list_node<T>* _next;T _val;list_node(const T& val = T())  //构造函数,初始化节点:_prev(nullptr),_next(nullptr),_val(val){}};template<class T,class Ref,class Ptr>   //三个模板参数struct list_iterator   //封装迭代器{typedef list_node<T> Node;typedef list_iterator<T, Ref, Ptr> self;Node* _node;list_iterator(Node*node)   //构造函数,单参数的构造函数支持隐式类型转换:_node(node){}//重载 * ++ -- != == ->Ref operator*() const{return _node->_val;}Ptr operator->() const{return &_node->_val;}self& operator++()   //前置++{_node = _node->_next;return *this;}self operator++(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& lt) const{return _node != lt._node;}bool operator==(const self& lt) const{return _node == lt._node;}};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迭代器void empty_init()  //因为构造函数和拷贝构造都会初始化头节点,所以就写成一个函数了{_head = new Node;_head->_prev = _head;_head->_next = _head;_sz = 0;}list()  //构造函数{empty_init();}//普通迭代器iterator begin(){return _head->_next;}iterator end(){return _head;}//const迭代器const_iterator begin() const{return _head->_next;}const_iterator end() const{return _head;}iterator insert(iterator pos, const T& x)  //在pos之前插入{Node* newnode = new Node(x);Node* cur = pos._node;Node* prev = cur->_prev;prev->_next = newnode;newnode->_prev = prev;newnode->_next = cur;cur->_prev = newnode;_sz++;return newnode;}iterator erase(iterator pos)   //删除pos位置,注意删除的时候不能把头节点也删了,所以要做pos检查{assert(pos != end());Node* cur = pos._node;Node* prev = cur->_prev;Node* next = cur->_next;prev->_next = next;next->_prev = prev;delete cur;_sz--;return next;   //库里规定返回删除节点的下一个节点}void push_back(const T& x)  //尾插{insert(end(), x);}void push_front(const T& x)  //头插{insert(begin(), x);}void pop_back()  //尾删{erase(--end());}void pop_front()  //头删{erase(begin());}void clear()  //清楚除了头节点以外的所有节点{iterator it = begin();while (it != end()){it=erase(it);}_sz = 0;}~list()  //析构函数{clear();delete _head;_head = nullptr;}list(const list<T>& lt)   //拷贝构造{empty_init();for (auto& e : lt){push_back(e);}}void swap(list<T>& lt){std::swap(_head, lt._head);std::swap(_sz, lt._sz);}list<T>& operator=(list<T> lt)  //赋值重载{swap(lt);return *this;}private:Node* _head;  //头节点size_t _sz;   //记录链表的长度};}

🐬🤖本篇文章到此就结束了, 若有错误或是建议的话,欢迎小伙伴们指出;🕊️👻

😄😆希望小伙伴们能支持支持博主啊,你们的支持对我很重要哦;🥰🤩

😍😁谢谢你的阅读。😸😼

 

 

 

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

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

相关文章

缓存数据一致性探究

缓存数据一致性探究 缓存是一种较低成本提升系统性能的方式&#xff0c;自它面世第一天起就备受广大开发者的喜爱。然而正如《人月神话》中的那句经典的“没有银弹”中所说&#xff0c;软件工程的设计没有银弹。 就像每一次发布上线修复问题的同时&#xff0c;也极易引入新的问…

[centos]安装mysql8.0.26

1、首先&#xff0c;根据自己的机子到MySQL官网下载对应的数据库https://dev.mysql.com/downloads/mysql/ 2、卸载mariadb&#xff0c;并解压Mysql 3、安装 rpm -ivh mysql-community-common-8.0.26-1.el7.x86_64.rpm --nodeps rpm -ivh mysql-community-libs-8.0.26-1.el7.x…

RT-Thread 学习-Env开发环境搭建(一)

Env是什么 Env 是 RT-Thread 推出的开发辅助工具&#xff0c;针对基于 RT-Thread 操作系统的项目工程&#xff0c;提供编译构建环境、图形化系统配置及软件包管理功能。 其内置的 menuconfig 提供了简单易用的配置剪裁工具&#xff0c;可对内核、组件和软件包进行自由裁剪&…

idea新建xml模板设置,例如:mybatis-config

在idea怎么新建mapper.xml文件&#xff0c;具体操作步骤和结果如下&#xff0c;其他文件也是可以自定义模板的流程和步骤一致&#xff01; 效果如下&#xff1a; 步骤如图&#xff1a; step1&#xff1a; step2&#xff1a; 文件内容&#xff1a; <?xml version"…

udp传输大数据的原理和相关问题注意事项

UDP协议本身不提供大数据传输的分片、重组、丢包重传等功能&#xff0c;因此需要对大数据传输进行特殊处理。以下是UDP传输大数据一些常见的处理方法。 &#xff08;1&#xff09;对大文件进行分块&#xff1a;将大文件划分为多个小块进行传输&#xff0c;每个小块都可以使用UD…

IntelliJ IDEA 2023.2 新版本即将发布,拥抱 AI

IntelliJ IDEA 近期连续发布多个EAP版本&#xff0c;官方在对用户体验不断优化的同时&#xff0c;也新增了一些不错的功能&#xff0c;尤其是人工智能助手补充&#xff0c;AI Assistant&#xff0c;相信在后续IDEA使用中&#xff0c;会对开发者工作效率带来不错的提升。 以下是…

安卓:Fragment

目录 一、Fragment介绍 二、Fragment的使用方式 &#xff08;一&#xff09;、Fragment静态添加&#xff1a; 静态添加例子&#xff1a; FirstFragment &#xff1a; MainActivity: main_activity: fragment_first: 静态添加的总结&#xff1a; &#xff08;二&…

Java-通过IP获取真实地址

文章目录 前言功能实现测试 前言 最近写了一个日志系统&#xff0c;需要通过访问的 IP 地址来获取真实的地址&#xff0c;并且存到数据库中&#xff0c;我也是在网上看了一些文章&#xff0c;遂即整理了一下供大家参考。 功能实现 这个是获取正确 IP 地址的方法&#xff0c;可…

域内信息收集

将网络中多台计算机逻辑上组织到一起进行集中管理&#xff0c;这种区别于工作组的逻辑环境叫 做域。域是由域控制器(Domain Controller)和成员计算机组成&#xff0c;域控制器就是安装了活动 目录(Active Directory)的计算机。活动目录提供了存储网络上对象信息并使用网络使用该…

uniapp H5预览PDF文件

1&#xff0c;下载资源后hybrid文件存放在static静态文件里 (点击这里去下载文件) 2&#xff0c;pdf预览页面配置 <template><view style"width: 100vh;"><web-view :src"pdfUrl"></web-view></view> </template><…

全面深入理解MySQL自增锁

&#x1f497;推荐阅读文章&#x1f497; &#x1f338;JavaSE系列&#x1f338;&#x1f449;1️⃣《JavaSE系列教程》&#x1f33a;MySQL系列&#x1f33a;&#x1f449;2️⃣《MySQL系列教程》&#x1f340;JavaWeb系列&#x1f340;&#x1f449;3️⃣《JavaWeb系列教程》…

【HarmonyOS】元服务隐私协议开发指导样例

【关键字】 隐私、弹窗、元服务、协议 【介绍】 每个元服务必须提供隐私声明&#xff0c;否则将导致提交元服务发布上架时&#xff0c;审核无法通过。隐私声明的具体要求请参见隐私声明规范。用户使用元服务前&#xff0c;必须引导其了解隐私声明信息&#xff0c;获取用户授权…

C#被指定窗体的MdiParent的窗体不是MdiContainer

工作的时候遇到一个问题&#xff1a; 被指定窗体的MdiParent的窗体不是MdiContainer 这个问题的原因是父窗体的IsMdiContainer 属性设置为false导致的。将此属性设置为true&#xff0c;即可解决此问题。有两种方式设置窗体的IsMdiContainer 属性。 第一种&#xff0c;在父窗口…

python实现拼多多商品详情接口API

最近工作需要用到拼多多的一些接口&#xff0c;官方竟然没有提供&#xff0c;python的sdk&#xff0c;于是就自己简单的写了一个商品SKU接口的api。 1、代码 #!/usr/bin/python3# -*- coding: utf-8 -*-# Time : 2020/3/29 0021 下午 19:40# Author : xiaozhi&#xff01;…

「深度学习之优化算法」(十六)万有引力算法

1. 万有引力算法简介 (以下描述,均不是学术用语,仅供大家快乐的阅读) 万有引力算法(Gravitational Search Algorithm)是受物体之间的万有引力启发而提出的算法。算法提出于2008(2009)年,时间不长,不过相关的文章和应用已经相对较多,也有不少的优化改进方案。   万…

apple pencil二代平替笔哪个好用?苹果平板平替笔排行

光是一款Apple Pencil&#xff0c;就卖到了接近一千多块&#xff0c;信息对于很多人来说都觉得太贵了。事实上&#xff0c;由于平替电容笔的性价比也非常高&#xff0c;因此它还是值得一试的。不管是学习&#xff0c;写作&#xff0c;还是绘画&#xff0c;这支平替电容笔&#…

自洽性改善语言模型中的思维链推理7.13、7.14

自洽性改善语言模型中的思维链推理 摘要介绍对多样化路径的自洽实验实验设置主要结果当CoT影响效率时候&#xff0c;SC会有所帮助与现有方法进行比较附加研究 相关工作总结 原文&#xff1a; 摘要 本篇论文提出了一种新的编码策略——自洽性&#xff0c;来替换思维链中使用的…

AI绘画 | 迷人武士美少女战士作品集

今天用Midjourney生成了质量极高的美少女武士后续会作为固定栏目来分享美图接下来请欣赏作品 提示词分享&#xff1a;1.an asian girl dressed in samurai style, in the style of anime aesthetic, trick of the eye paintings, dollcore, light red and black, resin, 8k, ex…

数据结构--图的基本操作

数据结构–图的基本操作 使用的存储模式&#xff1a; 图的基本操作&#xff1a; • Adjacent(G,x,y)&#xff1a;判断图G是否存在边<x, y>或(x, y)。 • Neighbors(G,x)&#xff1a;列出图G中与结点x邻接的边。 • InsertVertex(G,x)&#xff1a;在图G中插入顶点x。 • …

Apache HTTPD 多后缀解析漏洞

Apache HTTPD 多后缀解析漏洞 一、环境搭建二、漏洞原理三、漏洞复现 一、环境搭建 如下介绍kali搭建的教程 cd ~/vulhub/httpd/apache_parsing_vulnerability // 进入指定环境 docker-compose up -d // 启动环境docker-compose ps使用这条命令查看当前正在运行的环境 打开…