C++:list增删查改模拟实现
C++:list增删查改模拟实现
- 前言
- 一、list底层双链表验证、节点构造
- 1.1 list底层数据结构
- 1. 2 节点构造
- 二、迭代器封装实现(重点、难点)
- 2.1 前置说明
- 2.2 迭代器实现
- 三、list实现
- 3.1 基本框架
- 3.2 迭代器和const迭代器
- 3.2 构造函数、析构函数、拷贝构造、赋值重载
- 3.2.1 构造函数
- 3.2.2 析构函数
- 3.2.3 拷贝构造
- 3.2.4 赋值重载
- 3.3 任意位置插入、任意位置删除、尾插、尾删、头插、头删
- 3.3.1任意位置插入
- 3.3.2任意位置插入删除
- 3.3.3 尾插、尾删、头插、头删
- 四、list功能完善
- 4.1 迭代器operator->()
- 4.2 打印数据
- 五·、所有代码以及测试用例
前言
本篇博客采用SGI版本,同时考虑到看到此篇博客大部分为初学者,为此博主仅仅给出有用片段。C++:list增删查改模拟实现就是用C++复写双链表,非常简单。难点主要在迭代器实现
一、list底层双链表验证、节点构造
1.1 list底层数据结构
list底层使用什么数据结构来实现的呢?我们先来看看SGI库中list的成员函数和初始化吧。

我们发现list实现中,只有node一个成员变量。构造函数构造出一个头尾相连的哨兵位。同时验证了list底层是一个带哨兵位的双链表。
1. 2 节点构造
节点和双链表一样有三个成员:节点数据、指向前一个节点(prev)、指向后一个节点(next)。
//节点
template<class T>
struct List_node
{T _data;List_node<T>* _prev;List_node<T>* _next;List_node(const T& x = T()):_data(x),_prev(nullptr),_next(nullptr){}
};
小tips:
- 我们这里类名和库中一样(List_node),后续在其他类中使用时在typedef。
- 这里类名使用struct而不是class原因在于struct默认访问权限为公有,后续其他类只都需要大量使用。如果使用class需要使用大量友元类,过于繁琐。
二、迭代器封装实现(重点、难点)
2.1 前置说明
- 我们知道迭代器的最大用途便是遍历数据。但何时停在,迭代器指向空间存储数据时是什么…导致我们需要使用!=、++、*等操作。但自定义类型是无法支持使用这些操作符的。为此给出的解决办法是:封装加运算符重载。
- 迭代器使用上分为普通迭代器和const迭代器(还分为单向迭代器、双向迭代器和随机访问迭代器)。其中一种最简单的实现方式就是实现两个类。但。。。我们知道两个迭代器的不同之处在于const迭代器无法修改数据,只是j相关借口(这里有*、->)不同而已便实现两个类未免过于“小题大做”。
所以接下来我们来看看库中是如何巧妙的解决这个问题吧!

2.2 迭代器实现
前置说明中以及解决了如何实现一个类达到目的。接下来的实现过于简单就不单独说明了。
//迭代器封装
template<class T, class Ref, class Ptr>
struct __list_iterator
{typedef List_node<T> Node;//节点类名重命名//由于迭代器实现中,如++、--等重载函数返回值类型为__list_iterator,名字过长,这里我们重命名self意味自身typedef __list_iterator<T,Ref, Ptr> self;Node* _node;//成员变量__list_iterator(Node* node)//构造出一个节点:_node(node){}//前置++self& operator++(){_node = _node->_next;return *this;}//前置--self& operator--(){_node = _node->_prev;return *this;}//后置++self operator++(int){self tmp(*this);_node = _node->_next;return tmp;}//后置--self operator--(int){self tmp(*this);_node = _node->_prev;return tmp;}//解引用操作符重载Ref operator*(){return _node->_data;}//用于访问迭代器指向对象中存储的是自定义类型Ptr operator->(){return &_node->_data;}bool operator!=(const self& s){return _node != s._node;}bool operator==(const self& s){return _node == s._node;}
};
三、list实现
3.1 基本框架
list模拟中,我们和库中一样,给出一个头节点_head、_size两个成员变量。同时我们将节点、迭代器进行重命名。迭代器重命名不是单纯图方便,更重要的是提供统一接口(例如sting、vector、set等接口都一样),屏蔽底层的变量和成员函数属性,实现过程和差异。
//list模拟实现
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;
private:Node* _head;//头节点int _size;
};
3.2 迭代器和const迭代器
下面是begin()、end()指向一个有效双线表的位置。

实现:
const_iterator begin()const
{//return const_iterator(_head->_next);或return _head->_next;//单参数类型支持隐式类型转换
}const_iterator end()const
{return _head;
}iterator begin()
{return _head->_next;
}iterator end()
{return _head;
}
3.2 构造函数、析构函数、拷贝构造、赋值重载
3.2.1 构造函数
构造函数的实现和开头中看到的一样:构造函数中调用一个函数(empty_Init)来是实现。empty_Init()用来完成初始化。(empty_Init()函数后续其他接口也要调用)
//初始化
void empty_Init()
{_head = new Node;_head->_next = _head;_head->_prev = _head;_size = 0;
}//无参构造
List()
{empty_Init();
}
3.2.2 析构函数
析构函数我们调用一个clear函数来将数据全部清空,在将_head变量释放。
//析构函数
~List()
{clear();delete _head;_head = nullptr;
}void clear()
{iterator it = begin();while (it != end()){it = erase(it);}
}
3.2.3 拷贝构造
拷贝构造时首先要初始化出一个节点,然后将需要拷贝的数据依次尾插即可(尾插接口后续给出实现)
//拷贝构造
List(const List<T>& It)
{empty_Init();for (auto e : It){push_back(e);}
}
3.2.4 赋值重载
赋值重载有很多方式。比较简单的参数我们直接传值,然后将待赋值的变量和传值传参省生成的临时变量的数据进行交换即可。
void swap(const List<T>& It)
{std::swap(_head, It._head);
}//赋值重载
List<T>& operator=(const List<T> It)
{swap(It);return *this;
}
3.3 任意位置插入、任意位置删除、尾插、尾删、头插、头删
3.3.1任意位置插入
首先new出待插入节点newnode,然后将pos前一个节点prev、newnode、pos相连。最后++_size即可。

iterator insert(iterator pos, const T& x)
{Node* cur = pos._node;Node* prev = cur->_prev;Node* newnode = new Node(x);prev->_next = newnode;newnode->_prev = prev;newnode->_next = cur;cur->_prev = newnode;_size++;return newnode;
}
3.3.2任意位置插入删除
将待删除数据的前后节点先保存起来,然后将删除pos处节点,再将前后节点连接起来。最后–_size即可。
iterator erase(iterator pos)
{Node* cur = pos._node;Node* prev = cur->_prev;Node* next = cur->_next;delete cur;prev->_next = next;next->_prev = prev;_size--;return next;
}
3.3.3 尾插、尾删、头插、头删
尾插、尾删、头插、头删都是复用任意位置插入、任意位置删除接口。
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());
}
四、list功能完善
4.1 迭代器operator->()
我们先来看看下面这段代码对吗?
struct AA
{AA(int a1 = 0, int a2 = 0):_a1(a1),_a2(a2){}int _a1;int _a2;
};void test_list3()
{list<AA> lt;lt.push_back(AA(1, 1));lt.push_back(AA(2, 2));lt.push_back(AA(3, 3));list<AA>::iterator it = lt.begin();while (it != lt.end()){cout<<*it<<" ";++it;}cout << endl;
}
由于list没有重载<<,所以对存储的是自定义类型之间访问会编译报错。
那我们重载下<<运算符不就行了吗?很不幸的是C++库在list中不支持<<,很大原因也在于编译器不知到我们如何取数据
所以对于自定义类型我们可以先解引用在去访问成员,也可以在迭代器中重载operator->()函数。但需要注意的是编译器隐藏了一个->,具体原生行为如下:
struct AA
{AA(int a1 = 0, int a2 = 0):_a1(a1),_a2(a2){}int _a1;int _a2;
};void test_list3()
{list<AA> lt;lt.push_back(AA(1, 1));lt.push_back(AA(2, 2));lt.push_back(AA(3, 3));list<AA>::iterator it = lt.begin();while (it != lt.end()){//cout << (*it)._a1<<" "<<(*it)._a2 << endl;cout << it->_a1 << " " << it->_a2 << endl;//上面编译器访问成员变量的真正行为如下://cout << it.operator->()->_a1 << " " << it.operator->()->_a1 << endl;++it;}cout << endl;
}
4.2 打印数据
//大多数情况模板是class还是typename是一样的,但当有未实例化模板时,必须使用typename
//template<typename T>
void print_list(const list<T>& lt)
{// list<T>未实例化的类模板,编译器不能去他里面去找// 编译器就无法list<T>::const_iterator是内嵌类型,还是静态成员变量// 前面加一个typename就是告诉编译器,这里是一个类型,等list<T>实例化// 再去类里面去取typename list<T>::const_iterator it = lt.begin();while (it != lt.end()){cout << *it << " ";++it;}cout << endl;
}
优化:上面打印数据是针对list,下面是针对容器的。
// 模板(泛型编程)本质,本来应该由我们干的活交给编译器
template<typename Container>
void print_container(const Container& con)
{typename Container::const_iterator it = con.begin();while (it != con.end()){cout << *it << " ";++it;}cout << endl;
}
五·、所有代码以及测试用例
giteeC++:list增删查改模拟实现代码以及测试用例
推荐:giteeC++:list增删查改模拟实现代码(最终版本、感觉版本!!!)
相关文章:
C++:list增删查改模拟实现
C:list增删查改模拟实现 前言一、list底层双链表验证、节点构造1.1 list底层数据结构1. 2 节点构造 二、迭代器封装实现(重点、难点)2.1 前置说明2.2 迭代器实现 三、list实现3.1 基本框架3.2 迭代器和const迭代器3.2 构造函数、析构函数、拷贝构造、赋值…...
基于阿里云服务网格流量泳道的全链路流量管理(二):宽松模式流量泳道
作者:尹航 在前文基于阿里云服务网格流量泳道的全链路流量管理(一):严格模式流量泳道中,我们介绍了使用服务网格 ASM 的严格模式流量泳道进行全链路灰度管理的使用场景。该模式对于应用程序无任何要求,只需…...
ubuntu 18.04 共享屏幕
用于windows远程ubuntu 1. sudo apt install xrdp 2. 配置 sudo vim /etc/xrdp/startwm.sh 把最下面的test和exec两行注释掉,添加一行 gnome-session 3.安装dconf-editor : sudo apt-get install dconf-editor 关闭require encrytion org->gnome->desktop…...
第十三节TypeScript 元组
1、简介 我们知道数组中元素的数据类型一般都是相同的(any[]类型的数组可以不同),如果存储的元素类型不同,则需要使用元组。 元组中允许存储不同类型的元素,元组可以作为参数传递给函数。2、创建元组的语法格式&#x…...
基于Java (spring-boot)的仓库管理系统
一、项目介绍 本系统的使用者一共有系统管理员、仓库管理员和普通用户这3种角色: 1.系统管理员:通过登录系统后,可以进行管理员和用户信息的管理、仓库和物品分类的管理,以及操作日志的查询,具有全面的系统管理权限。 2.仓库管理…...
SQL面试题挑战06:互相关注的人
目录 问题:SQL解答: 问题: 现在有一张relation表,里面只有两个字段:from_user和to_user,代表关注关系从from指向to,即from_user关注了to_user。现在要找出互相关注的所有人。 from_user to_…...
LSTM和GRU的区别
LSTM(Long Short-Term Memory)和GRU(Gated Recurrent Unit)都是循环神经网络(RNN)的变体,旨在解决传统RNN中的梯度消失和梯度爆炸的问题,使网络能够更好地处理长期依赖关系。 以下是…...
算法基础之数字三角形
数字三角形 核心思想:线性dp 集合的定义为 f[i][j] –> 到i j点的最大距离 从下往上传值 父节点f[i][j] max(f[i1][j] , f[i1][j1]) w[i][j] 初始化最后一层 f w #include <bits/stdc.h>using namespace std;const int N 510;int w[N][N],f[N][…...
蓝桥杯宝藏排序题目算法(冒泡、选择、插入)
冒泡排序: def bubble_sort(li): # 函数方式for i in range(len(li)-1):exchangeFalsefor j in range(len(li)-i-1):if li[j]>li[j1]:li[j],li[j1]li[j1],li[j]exchangeTrueif not exchange:return 选择排序: 从左往右找到最小的元素,放在起始位置…...
如何使用Docker部署Dashy并无公网ip远程访问管理界面
文章目录 简介1. 安装Dashy2. 安装cpolar3.配置公网访问地址4. 固定域名访问 简介 Dashy 是一个开源的自托管的导航页配置服务,具有易于使用的可视化编辑器、状态检查、小工具和主题等功能。你可以将自己常用的一些网站聚合起来放在一起,形成自己的导航…...
【接口测试】如何定位BUG的产生原因
我们从在日常功能测试过程中对UI的每一次操作说白了就是对一个或者多个接口的一次调用,接口的返回的内容(移动端一般为json)经过前端代码的处理最终展示在页面上。http接口是离我们最近的一层接口,web端和移动端所展示的数据就来自于这层,那么…...
JavaScript 中的短路求值(if语句简洁写法--逻辑运算符||和的高级用法)
在JavaScript中,Short-Circuit Evaluation(短路求值)是一种逻辑运算的行为,其中表达式的求值在达到不必要的部分时就提前终止(所以短路一词非常贴切)。这种行为可以通过逻辑运算符(例如&&am…...
普本毕业,还有逆风翻盘的机会吗?
作为普通二本的本科生,从踏入大学开始,我一直在不断寻找自己感兴趣的行业和职业方向。 在这里,我想给大家分享一些我从校园走向工作整个学习和求职过程,以及其中的酸甜苦辣,希望这些经历可以给各位学弟学妹一些鼓励和…...
spark:RDD编程(Python版)
RDD运行原理 RDD设计背景 许多选代目前的MapReduce框架都是把中间结果写入到稳定存储 (比如磁盘)中带来了大量的数据复制、磁盘IO和序列化开销 RDD就是为了满足这种需求而出现的,它提供了一个抽象的数据架构,我们不必担心底层数据的分布式特性…...
中国元宇宙论坛暨常孝元宇宙发布会即将在京举行
中国元宇宙论坛暨常孝元宇宙发布会将于2024年1月9日在北京科技会堂盛大开启。本次论坛汇聚业内顶尖专家、学者和企业代表,共同探讨中国元宇宙、常孝元宇宙《神由都城》的未来发展、技术创新和应用场景。此次发布会将颠覆我们对数字世界的认知,带来前所未有的体验。 《神由都城》…...
华为认证 | 云计算方向HCIE有效期多久?实验报名费多少?
云计算技术已经成为了企业和个人发展的重要网络技术支撑。 而在这个领域中,华为HCIE云计算证书也成为了越来越多人追求的敲门砖。 然而,很多人对于这个证书的有效期以及实验报名费并不清楚。 下面将为你详细解答这些问题。 01 云计算方向HCIE有效期多…...
动物分类识别教程+分类释义+界面展示
1.项目简介 动物分类教程分类释义界面展示 动物分类是生物学中的一个基础知识,它是对动物进行分类、命名和描述的科学方法。本教程将向您介绍动物分类的基本原则和方法,并提供一些常见的动物分类释义。 动物分类的基本原则 动物分类根据动物的形态、…...
【Java动态代理如何实现】
✅Java动态代理如何实现 ✅JDK动态代理和Cglib动态代理的区别 ✅拓展知识仓✅静态代理和动态代理的区别✅动态代理的用途✅Spring AOP的实现方式📑JDK 动态代理的代码段📑Cglib动态代理的代码块 ✅注意事项: 在Java中,实现动态代理…...
数据库(部分函数)
函数: 单行函数:会对查询中的每一数据进行处理 字符函数 length(列名) select name, 日期函数: now() 年月日时分秒 curdate() 年月日 curtime()时分秒 …...
基于Vite+Vue3 给项目引入Axios
基于ViteVue3 给项目引入Axios,方便与后端进行通信。 系列文章指路👉 系列文章-基于Vue3创建前端项目并引入、配置常用的库和工具类 文章目录 安装依赖新建src/config/config.js 用于存放常用配置进行简单封装解决跨域问题调用尝试 安装依赖 npm install axios …...
Spring Boot面试题精选汇总
🤟致敬读者 🟩感谢阅读🟦笑口常开🟪生日快乐⬛早点睡觉 📘博主相关 🟧博主信息🟨博客首页🟫专栏推荐🟥活动信息 文章目录 Spring Boot面试题精选汇总⚙️ **一、核心概…...
【OSG学习笔记】Day 16: 骨骼动画与蒙皮(osgAnimation)
骨骼动画基础 骨骼动画是 3D 计算机图形中常用的技术,它通过以下两个主要组件实现角色动画。 骨骼系统 (Skeleton):由层级结构的骨头组成,类似于人体骨骼蒙皮 (Mesh Skinning):将模型网格顶点绑定到骨骼上,使骨骼移动…...
Kafka入门-生产者
生产者 生产者发送流程: 延迟时间为0ms时,也就意味着每当有数据就会直接发送 异步发送API 异步发送和同步发送的不同在于:异步发送不需要等待结果,同步发送必须等待结果才能进行下一步发送。 普通异步发送 首先导入所需的k…...
DingDing机器人群消息推送
文章目录 1 新建机器人2 API文档说明3 代码编写 1 新建机器人 点击群设置 下滑到群管理的机器人,点击进入 添加机器人 选择自定义Webhook服务 点击添加 设置安全设置,详见说明文档 成功后,记录Webhook 2 API文档说明 点击设置说明 查看自…...
【前端异常】JavaScript错误处理:分析 Uncaught (in promise) error
在前端开发中,JavaScript 异常是不可避免的。随着现代前端应用越来越多地使用异步操作(如 Promise、async/await 等),开发者常常会遇到 Uncaught (in promise) error 错误。这个错误是由于未正确处理 Promise 的拒绝(r…...
springboot 日志类切面,接口成功记录日志,失败不记录
springboot 日志类切面,接口成功记录日志,失败不记录 自定义一个注解方法 import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;/***…...
DeepSeek源码深度解析 × 华为仓颉语言编程精粹——从MoE架构到全场景开发生态
前言 在人工智能技术飞速发展的今天,深度学习与大模型技术已成为推动行业变革的核心驱动力,而高效、灵活的开发工具与编程语言则为技术创新提供了重要支撑。本书以两大前沿技术领域为核心,系统性地呈现了两部深度技术著作的精华:…...
【iOS】 Block再学习
iOS Block再学习 文章目录 iOS Block再学习前言Block的三种类型__ NSGlobalBlock____ NSMallocBlock____ NSStackBlock__小结 Block底层分析Block的结构捕获自由变量捕获全局(静态)变量捕获静态变量__block修饰符forwarding指针 Block的copy时机block作为函数返回值将block赋给…...
【threejs】每天一个小案例讲解:创建基本的3D场景
代码仓 GitHub - TiffanyHoo/three_practices: Learning three.js together! 可自行clone,无需安装依赖,直接liver-server运行/直接打开chapter01中的html文件 运行效果图 知识要点 核心三要素 场景(Scene) 使用 THREE.Scene(…...
八、【ESP32开发全栈指南:UDP客户端】
1. 环境准备 安装ESP-IDF v4.4 (官方指南)确保Python 3.7 和Git已安装 2. 创建项目 idf.py create-project udp_client cd udp_client3. 完整优化代码 (main/main.c) #include <string.h> #include "freertos/FreeRTOS.h" #include "freertos/task.h&…...
