《C++:从代码到机器》:面试官:“说说 list 怎么模拟实现?” 我掏出这份代码,他点头了

《C++:从代码到机器》:面试官:“说说 list 怎么模拟实现?” 我掏出这份代码,他点头了

个人头像

✨ 孤廖:个人主页

🎯 个人专栏:《C++:从代码到机器》

🎯 个人专栏:《Linux系统探幽:从入门到内核》

🎯 个人专栏:《算法磨剑:用C++思考的艺术》

折而不挠,中不为下



在这里插入图片描述

文章目录

正文:

1.list的介绍与使用

1.1 list的介绍 list的介绍

list的文档介绍

在这里插入图片描述

1.2 list的使用

list中的接口比较多,此处类似,只需要掌握如何正确的使用,然后再去深入研究背后的原理,已达到可扩展的能力。以下为list中一些常见的重要接口。
1.2.1 list的构造
在这里插入图片描述
1.2.2 list iterator的使用
此处,大家可暂时将迭代器理解成一个指针,该指针指向list中的某个节点

【注意】:

begin与end为正向迭代器,对迭代器执行++操作,迭代器向后移动rbegin(end)与rend(begin)为反向迭代器,对迭代器执行++操作,迭代器向前移动
1.2.3 list capacity
在这里插入图片描述
1.2.4 list element access
在这里插入图片描述
1.2.5 list modifiers
在这里插入图片描述
list中还有一些操作,需要用到时大家可参阅list的文档说明。
1.2.6 list的迭代器失效
前面说过,此处大家可将迭代器暂时理解成类似于指针,迭代器失效即迭代器所指向的节点的无效,即该节点被删除了。因为list的底层结构为带头结点的双向循环链表,因此在list中进行插入时是不会导致list的迭代器失效的,只有在删除时才会失效,并且失效的只是指向被删除节点的迭
代器,其他迭代器不会受到影响。vector迭代器失效可以看我主页的一篇文章
《C++:从代码到机器》:vector 的坑只有 C++ 党懂?介绍使用 + 深度剖析 + 模拟实现,帮你全避开

代码解释:

#include<iostream>#include<list>usingnamespace std;// 存在问题的函数voidTestListIterator1(){int array[]={1,2,3,4,5,6,7,8,9,0}; list<int>l(array, array +sizeof(array)/sizeof(array[0]));auto it = l.begin();while(it != l.end()){// erase()函数执行后,it所指向的节点已被删除,因此it无效,在下一次使用it时,必须先给其赋值 l.erase(it);++it;}}// 改正后的函数voidTestListIterator(){int array[]={1,2,3,4,5,6,7,8,9,0}; list<int>l(array, array +sizeof(array)/sizeof(array[0]));auto it = l.begin();while(it != l.end()){ l.erase(it++);// it = l.erase(it);}}intmain(){// 可以取消下面的注释来测试函数// TestListIterator1();// TestListIterator();return0;}

2. list的模拟实现

2.1 模拟实现list

要模拟实现list,必须要熟悉list的底层结构以及其接口的含义,通过上面的学习,这些内容已基本掌握,现在我们来模拟实现list
希望大家有时间 可以自己实现一下底层 来增强理解
#pragmaonce#include<iostream>#include<assert.h>usingnamespace std;//模拟stl_list//带头双向循环链表namespace twg {template<classT>structlist_node{//数据域 T _data;//指针域 list_node<T>* next; list_node<T>* prev;list_node(const T& data =T()):_data(data),next(nullptr),prev(nullptr){}};template<classT,classRef,classPtr>structlist_iterator{typedef list_node<T> Node;typedef list_iterator<T, Ref, Ptr> Self; Node* _node;//原生指针//默认构造 list_iterator<T, Ref, Ptr>(Node* node):_node(node){}//重载运算符//前置 Self&operator++(){ _node = _node->next;return*this;} Self&operator--(){ _node = _node->prev;return*this;}//后置 Self operator++(int){ Self cur =*this; _node = _node->next;return cur;} Self operator--(int){ Self cur =*this; _node = _node->prev;return cur;}//重载* Ref operator*(){return _node->_data;} Ptr operator->(){return&_node->_data;}booloperator!=(const Self& s)const{return _node != s._node;}booloperator==(const Self& s)const{return _node == s._node;}};template<classT>classlist{typedef list_node<T> Node;typedef list_iterator<T, T&, T*> iterator;typedef list_iterator<T,const T&,const T*> const_iterator;typedef list<T> container;public:voidempty_init(){ Head =new Node; Head->next = Head; Head->prev = Head; _size =0;}//默认构造list(){empty_init();}//拷贝构造list(const list<T>& s){empty_init();for(auto& e : s){push_back(e);}}//析构~list(){clear();delete Head; Head =nullptr;}//清理 资源voidclear(){auto it =begin();while(it !=end()){ it =erase(it);}}//赋值 list<T>&operator=(const list<T>& s){ list<T>tmp(s);swap(tmp);return*this;}voidswap(container& s){ std::swap(s.Head, Head); std::swap(s._size, _size);}//普通迭代器//begin iterator begin(){return Head->next;//返回时本质是调用了iterator()的构造函数产生了临时对象}//end iterator end(){return Head;}//const 迭代器//begin const_iterator begin()const{return Head->next;//返回时本质是调用了iterator()的构造函数产生了临时对象} const_iterator end()const{return Head;}//insertvoidinsert(iterator pos,const T& x){ Node* cur = pos._node; Node* prev = cur->prev; Node* newnode =new Node; newnode->_data = x; newnode->next = cur; newnode->prev = prev; prev->next = newnode; cur->prev = newnode; _size++;}//erase iterator erase(iterator pos){ iterator tmp = pos;++tmp;assert(pos !=end()); Node* cur = pos._node; iterator next = tmp; cur->prev->next = cur->next; cur->next->prev = cur->prev;delete cur; cur =nullptr; _size--;return next;}//push_bakcvoidpush_back(const T& x){insert(end(), x);}//pop_backvoidpop_back(){assert(_size !=0);erase(--end());}voidprint1()const{auto it =begin();while(it !=end()){ cout <<*it <<" ";++it;} cout << endl;}voidprint2()const{auto it =begin();while(it !=end()){ cout << it->a <<" ";++it;} cout << endl;}boolempty()const{return _size ==0;} size_t size()const{return _size;}private: Node* Head; size_t _size;};voidtest1(){ list<int> l1; l1.push_back(1); l1.push_back(2); l1.push_back(3); l1.push_back(4); l1.push_back(5); l1.print1();}voidtest2(){structAA{int a =1;}; list<AA> l1; l1.push_back({1}); l1.push_back({2}); l1.push_back({3}); l1.push_back({4}); l1.push_back({5}); l1.print2(); l1.clear(); l1.print2();}}

list的模拟实现码云

2.2 list的反向迭代器

通过前面例子知道,反向迭代器的++就是正向迭代器的–,反向迭代器的–就是正向迭代器的++,因此反向迭代器的实现可以借助正向迭代器,即:反向迭代器内部可以包含一个正向迭代器,对正向迭代器的接口进行包装即可。

【代码解释】:

template<classIterator>classReverseIterator{typedeftypenameIterator::Ref Ref;typedeftypenameIterator::Ptr Ptr;typedef ReverseIterator<Iterator> Self;public:// 构造ReverseIterator(Iterator it):_it(it){}// 具有指针类似行为 Ref operator*(){ Iterator temp = _it;--temp;return*temp;} Ptr operator->(){return&(operator*());}// 迭代器运算符++ Self&operator++(){--_it;return*this;} Self operator++(int){ Self temp(*this);--_it;return temp;} Self&operator--(){++_it;return*this;} Self operator--(int){ Self temp(*this);++_it;return temp;}// 迭代器支持比较booloperator!=(const Self& l)const{return _it != l._it;}booloperator==(const Self& l)const{return _it == l._it;}private: Iterator _it;};

3. list与vector的对比

vector与list都是STL中非常重要的序列式容器,由于两个容器的底层结构不同,导致其特性以及应用场景不同,其主要不同如下:
在这里插入图片描述


结语

看到这里,你已经把 STL 里的 list 彻底“吃透”啦——从双向链表的底层逻辑,到迭代器的使用(连容易栽跟头的“失效坑”都摸清了),再到亲手模拟实现核心功能,最后还能清晰对比它和 vector 在“增删效率”“随机访问”“空间利用”上的差异。这些知识就像给你搭好了“序列式容器”的骨架,让你对 STL 的理解更扎实。

不过,STL 的容器家族里,还有**栈(stack)和队列(queue)**这对“特殊选手”等着我们探索~它们看似简单(只允许一端/两端操作),实则藏着巧妙的“适配器”设计(比如依赖其他容器实现自身逻辑),而且在“表达式求值”“广度优先搜索”等场景中是绝对的主力。下一篇,我们就来解锁栈和队列:从它们的接口用法,到适配器模式的底层原理,再到实战中的经典应用,带你把这对“容器搭档”玩明白。

要是现在对 list 的“迭代器模拟”“反向迭代器包装”还有模糊的地方,不妨再翻翻代码注释;想更直观感受 listvector 的性能差距,也可以自己写个测试(比如批量插入 10 万条数据)。欢迎在评论区分享你和 list 打交道的故事,或者聊聊你对栈和队列的好奇,咱们一起把 STL 从“会用”变成“用得溜”~

在这里插入图片描述

Read more

SpringBoot之统一数据返回格式

SpringBoot之统一数据返回格式

目录 统一数据返回格式 使用 定义测试接口 运行结果 问题解答(结合源码) 解决方法:对返回类型为String类型的字符串进行序列化 优点 之前我们实现的图书管理系统,函数的返回值格式都不统一,如果将这些函数的返回值格式都进行统一,将方便后续的返回和接收,但是如果我们挨个修改这些函数的返回值格式,有些麻烦,那么有没有别的好的办法呢?----有,就是下面我们要讲解的"SpringBoot之统一数据返回格式"。 统一数据返回格式 使用 统⼀的数据返回格式使⽤ @ControllerAdvice 和 ResponseBodyAdvice 的⽅式实现 @ControllerAdvice 表⽰控制器通知类。 定义类 ResponseAdvice , 实现 ResponseBodyAdvice 接⼝, 并在类上添加 @ControllerAdvice 注解。 @ControllerAdvice public class ResponseAdvice implements ResponseBodyAdvice { @Override public

By Ne0inhk

Ubuntu 20.04/22.04 下通过 NVM 安装 Node.js 22(LTS 稳定版)

引言 Node.js 是前端开发、后端服务开发的核心环境,而 NVM(Node Version Manager)作为跨平台的 Node.js 版本管理器,能轻松实现多版本 Node.js 切换、安装与卸载,避免版本冲突问题。本文将详细介绍在 Ubuntu 系统中通过 NVM 安装 Node.js 22(LTS 稳定版)的完整步骤,操作简单且适配主流 Ubuntu 版本,适合开发新手与进阶用户。 一、准备工作:安装依赖工具 curl Ubuntu 系统默认可能未预装 curl 工具,而后续安装 NVM 需要通过 curl 下载脚本,因此首先需执行以下命令安装 curl:

By Ne0inhk
告别适配难题:Oracle 迁移 KingbaseES SQL 语法快速兼容方案

告别适配难题:Oracle 迁移 KingbaseES SQL 语法快速兼容方案

引言 在数据库国产化替代的浪潮中,Oracle 迁移到 KingbaseES(金仓数据库)已经成为很多企业数字化转型的核心任务。而 SQL 语法适配是迁移过程中最关键的技术环节,直接影响项目效率、成本和系统稳定性。 KingbaseES 以内核级兼容为基础,Oracle 常用 SQL 语法的兼容度能达到 100%,就算有少量差异化场景,也有清晰可落地的适配方案,能帮企业实现“应用无感、平滑迁移”。下面结合官方兼容性文档和实际迁移案例,拆解 SQL 语法适配的核心要点、差异化场景解决方案和批量落地技巧,给数据库管理员和开发人员提供实用参考。 文章目录 * 引言 * 一、迁移前必懂:SQL 兼容性整体情况 * 二、核心适配场景:差异化语法解决方案(含代码示例) * (一)数据类型映射:大多零代码,特殊场景稍调整 * (二)函数差异:精准适配,语法大多兼容(含对比代码) * 1.

By Ne0inhk
Flutter 组件 easter 适配鸿蒙 HarmonyOS 实战:天文学节气算法,构建全球化复活节周期与民俗历法治理架构

Flutter 组件 easter 适配鸿蒙 HarmonyOS 实战:天文学节气算法,构建全球化复活节周期与民俗历法治理架构

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net Flutter 组件 easter 适配鸿蒙 HarmonyOS 实战:天文学节气算法,构建全球化复活节周期与民俗历法治理架构 前言 在鸿蒙(OpenHarmony)生态迈向全球化部署、涉及跨区域文化适配(I18n)及复杂变动日期计算的背景下,如何精确推演具备“阴阳历混合特性”的全球性节日(如复活节),已成为决定跨国类应用“运营确定性”的核心技术难点。在鸿蒙设备这类强调 AOT 极致性能与低功耗常驻服务(AOD)的环境下,如果应用依然依赖手动配置的“节日死表”,由于由于复活节日期在全球范围内的复杂游移性,极易由于由于配置滞后导致海外营销活动的时序错乱。 我们需要一种能够实现高精度天文学推演、支持百年尺度计算且具备纯 Dart 离线运作能力的历法预判方案。 easter 为 Flutter 开发者引入了基于高斯算法(Gauss's algorithm)或曼氏算法(Meeus&

By Ne0inhk