news 2026/5/5 2:22:32

list模拟实现

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
list模拟实现

个人主页:小则又沐风

个人专栏:<数据结构>

<竞赛专栏>

<C语言>
<C++>

<Linux>

座右铭

路虽远,行则将至;事虽难,做则必成

目录

前言

节点类

迭代器类

链表类

构造函数

析构函数

简单的容量的接口

迭代器

数据的插入和删除

insert

erase

clear

总结


前言

在之前的文章中,我们学习了C++库中的list接口的使用,今天我们来了解一下库中的list是怎么实现的,并且我们来模拟实现一下简单的list的接口

节点类

我们在数据结构中就知道,list的结构就像是一个一个的火车一样,所以我们在实现我们自己list的时候我们先把这个节点来封装成一个类

template<class T> struct listnode { listnode(const T& x=T()) { _val = x; _pre = nullptr; _next = nullptr; } listnode<T>* _pre; listnode<T>* _next; T _val; };

在这里我们使用的是类的模板,我们的这个节点的实现是依靠一个节点存储的数据,指向下一个节点和指向前一个结点的指针组成的

需要注意的是我们这个类是自定义的类型,所以编译器自带的默认构造函数是不可以满足我们的需求的,所以我们需要自己显示的实现构造函数,这个构造函数是非常的简单的,因为我们只需要造出一个节点不需要连接,所以我们指针都是一个个空指针

以上我们就把这个节点的类实现好了

现在我们来实现一下这个迭代器

迭代器类

我们在上一篇链表的文章中我们知道这个迭代器也是一个封装好的类,这是因为如果不把他封装好的话,我们想要像和其他的迭代器使用他的话,我们解引用节点得到的并不是节点的数据,而是这个节点,所以为了符合迭代器的使用的习惯,所以我们需要把迭代器进行封装,但是我们知道的是我们在之前的迭代器的实现的时候我们不仅仅实现了普通的迭代器,我们还实现了const的迭代器

所以在我们链表的迭代器的封装的过程难道我们需要把这个迭代器的实现写两份吗?并且我们知道的是这两串代码可是高度相似的

这时候我们就需要来进一步高级的运用我们的模板参数了

template<class T,class ref,class ptr >

在这里我的迭代器的模板参数并不是简简单单的一个,而是三个模板参数,现在我们详细介绍一下

这些模板参数有什么作用

我们知道我们在类实例化出对象的时候我们的模板类是需要传入模板的参数的,这时候我们就只需要const的传入的是const的参数,普通的传入普通的就行了,编译器会为我们自己实现出两份代码的

所以我们只需要把这个模板来封装好就好了

在我们的模板中这个ref指的是T&或者是const T&

ptr指的是T*或者是const T*

是不是恍然大悟

所以我们并不需要两段代码来实现迭代器了,只需要来用通用的模板参数就可以了

现在我们解决了代码重复率的问题,现在我们就正式来实现一下这个迭代器的类

我们知道的是这个所谓的迭代器就是一个节点的指针

typedef listnode<T> node; node* _node; listiterator(const listiterator& l) :_node(l._node) { }

迭代器的构造函数我只写了最常用的一个

ref operator*() { return _node->_val; } ptr operator->() { return &_node->_val; }

在这里我们就使用上了模板参数

self& operator++() { _node = _node->_next; return *this; } self operator++(int) { self temp(*this); _node = _node->_next; return temp; } self& operator--() { _node = _node->_pre; return *this; } self operator--(int) { self temp(*this); _node = _node->_pre; return temp; } bool operator!=(const self& l) { return !(_node == l._node); } bool operator==(const self& l) { return _node == l._node; }

在这里有人问了这个self是何方神圣?

这是我们的迭代器本身,如果我们直接写的话我们的函数的返回值就太长了

typedef listiterator<T, ref, ptr> self;

以上就是我们的准备工作了下面就是进入我们真正的实现链表的环节了

链表类

typedef listnode <T> node; typedef listiterator<T, T&, T*> iterator; typedef listiterator<T, const T&, const T*> const_iterator;

实现的是双向循环的链表

先来展示一下链表含的成员变量

private: node* head; size_t _size;

就是个这个第一个相当于我们的哨兵位节点,然后这个size是记录我们总共有多少个节点

构造函数

因为我们在之前我就已经封装好了我们的节点类,所以我们的构造函数的任务就是把节点连接并且构造出一个哨兵位节点

void empty_init() { head = new node; head->_next = head; head->_pre = head; _size = 0; } list() { empty_init(); } list(int n, const T& value = T()) { empty_init(); while (n--) { push_back(value); } } template <class Iterator> list(Iterator first, Iterator last) { empty_init(); while (first != last) { push_back(*first); first++; } } list(const list<T>& l) { empty_init(); for (auto e : l) { push_back(e); } } list<T>& operator=(const list<T> l) { empty_init(); swap(l); return *this; }

需要解释的是如果我们的链表是一个空链表的话(只有一个哨兵位)设计的是head的next和preve都是指向自己的

在这里我们都是通过复用push_back实现的所以我们的构造函数实现的是轻而易举的

我们来仔细的剖析一下这个赋值构造

在这里我们函数的参数并不是一个引用的传参数,所以我们的实参会进行拷贝构造,所以进入函数体内的是一个全新的链表,这时候我们把它夺舍了就行了,直接swap一下

在链表的swap只需要交换一下头节点就好了,注意需要交换一下数据的个数

void swap(list<T>& l) { std::swap(head,l.head); std::swap(_size,l._size); }

析构函数

~list() { node* cur = head->_next; while (cur != head) { node* next = cur->_next; delete cur; cur = next; } delete head; }

我们析构函数的存在的意义就是防止我们的内存泄漏的,所以我们需要把我们所有开辟的空间进行释放,因为我们的每一个的节点都是我们自己new出来的所以我们需要遍历链表一个个delete

简单的容量的接口

size_t size()const { return _size; } bool empty()const { return _size == 0; } T& front() { return head->_next->_val; } const T& front()const { return head->_next->_val; } T& back() { return head->_pre->_val; } const T& back()const { return head->_pre->_val; }

迭代器

iterator begin() { return iterator(head->_next); } iterator end() { return iterator(head); } const_iterator begin()const { return const_iterator(head->_next); } const_iterator end()const { return const_iterator(head); }

下面就是有难度的插入的接口了

数据的插入和删除

void push_back(const T& val) { insert(end(), val); } void pop_back() { erase(--end()); } void push_front(const T& val) { insert(begin(), val); } void pop_front() { erase(begin()); }

在这几个接口的实现我们可以看到是高度的复用了erase和insert的函数

所以我们的重头戏就是来实现着两个函数

insert

// 在pos位置前插入值为val的节点 iterator insert(iterator pos, const T& val) { node* cur = pos._node; node* pcur = cur->_pre; node* newnode = new node(val); pcur->_next = newnode; cur->_pre = newnode; newnode->_next=cur; newnode->_pre = pcur; _size++; return iterator(newnode); }

注意我们的函数的参数是一个迭代器加上一个数据

我们insert实现的在pos的之前来插入数据的

我们的目的就是在pos的前面插入一个节点

这样就搞定了

上面的代码就是这样的思路

注意需要返回插入节点的迭代器别忘了size++

erase

// 删除pos位置的节点,返回该节点的下一个位置 iterator erase(iterator pos) { node* cur = pos._node; node* pcur = cur->_pre; node* ncur = cur->_next; pcur->_next = ncur; ncur->_pre = pcur; delete cur; _size--; return iterator(ncur); }

修改完指针后

deletepos节点

然后调整一下数据的个数

并且返回下一个节点

clear

void clear() { auto cur = begin(); while (cur != end()) { cur=erase(cur); } }

把链表的数据全删除并且释放节点

总结

本次内容围绕 C++ 手写链表展开,从节点类、迭代器类的基础实现,到链表类的封装(含构造 / 析构、容量接口),再到核心操作 insert、erase、clear 的实现逻辑,完整复刻了 STL 链表的核心框架。

在之后我们会来了解怎么来封装堆,栈,队列.

谢谢大家的观看!!!

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/5 2:20:56

从零部署Autoxhs:AI自动化生成小红书笔记的架构、调优与避坑指南

1. 项目概述&#xff1a;一个能自动生成小红书笔记的AI工具最近在AI内容生成这个圈子里&#xff0c;一个叫“Gikiman/Autoxhs”的项目热度挺高。简单来说&#xff0c;这是一个基于Python的开源工具&#xff0c;它的核心目标就是帮你自动化生成小红书风格的图文笔记。如果你是个…

作者头像 李华
网站建设 2026/5/5 2:19:26

基于机器视觉的芯片引脚检测与分拣系统边缘连接【附代码】

✨ 本团队擅长数据搜集与处理、建模仿真、程序设计、仿真代码、EI、SCI写作与指导&#xff0c;毕业论文、期刊论文经验交流。 ✅ 专业定制毕设、代码 ✅ 如需沟通交流&#xff0c;查看文章底部二维码&#xff08;1&#xff09;基于梯度幅值直方图的自适应双阈值Canny边缘检测&a…

作者头像 李华
网站建设 2026/5/5 2:11:09

AI赋能开发:让快马智能生成trea国际版交易风控系统代码

在开发trea国际版交易风控系统时&#xff0c;我发现AI辅助开发能大幅提升效率。特别是像InsCode(快马)平台这样集成了AI模型的平台&#xff0c;让复杂金融逻辑的代码实现变得简单直观。下面分享我如何用AI思路构建交易风控模块的关键环节&#xff1a; 模拟数据生成 传统开发需要…

作者头像 李华
网站建设 2026/5/5 2:06:28

MuseTalk 1.5版本对比:核心改进与价值分析

MuseTalk 1.5版本对比&#xff1a;核心改进与价值分析 【免费下载链接】MuseTalk MuseTalk: Real-Time High Quality Lip Synchorization with Latent Space Inpainting 项目地址: https://gitcode.com/gh_mirrors/mu/MuseTalk 技术架构优化与性能提升表现 MuseTalk作为…

作者头像 李华