c++领域展开第十八幕——STL(list容器的了解和使用以及手撕模拟)超详细!!!!

c++领域展开第十八幕——STL(list容器的了解和使用以及手撕模拟)超详细!!!!
在这里插入图片描述

文章目录

前言

本专栏上一篇博客把vector的有关实现和细节问题都讲解完毕
今天我们来学习 stl 库的另外一个容器——list
从认识到使用到手撕实现,我来手把手教你
fellow me

一、list的介绍和使用

1.1 list的介绍

list文档

在这里插入图片描述


文档链接在上面啦,大家可以翻译看看
双向循环链表图,list就相当于我们数据结构学习的双向循环链表
只不过在stl库里面给它进行了封装

在这里插入图片描述

1.2 list的使用

list中的接口比较多,此处类似,只需要掌握如何正确的使用,然后再去深入研究背后的原理,已达到可扩展的能力。以下为list中一些常见的重要接口。

1.2.1 list的构造

构造函数((constructor))
list (size_type n, const value_type& val = value_type())——————构造的list中包含n个值为val的元素
list() ————————————构造空的list
list (const list& x) ——————拷贝构造函数
list (InputIterator first, InputIterator last)————用[first, last)区间中的元素构造list

代码展示

 list<int> l1;// 构造空的l1 list<int>l2(4,100);// l2中放4个值为100的元素 list<int>l3(l2.begin(), l2.end());// 用l2的[begin(), end())左闭右开的区间构造l3 list<int>l4(l3);// 用l3拷贝构造l4// 以数组为迭代器区间构造l5int array[]={16,2,77,29}; list<int>l5(array, array +sizeof(array)/sizeof(int));// 列表格式初始化C++11 list<int> l6{1,2,3,4,5};

1.2.2 list iterator的使用

此处,大家可暂时将迭代器理解成一个指针,该指针指向list中的某个节点

函数声明——————接口说明
begin + end————返回第一个元素的迭代器+返回最后一个元素下一个位置的迭代器
rbegin + rend————返回第一个元素的reverse_iterator,即end位置,返回最后一个元素下一个位置的reverse_iterator,即begin位置

【注意】

  1. begin与end为正向迭代器,对迭代器执行++操作,迭代器向后移动
  2. rbegin(end)与rend(begin)为反向迭代器,对迭代器执行++操作,迭代器向前移动

代码展示

// 注意这里调用的是list的 begin() const,返回list的const_iterator对象for(list<int>::const_iterator it = l.begin(); it != l.end();++it){ cout <<*it <<" ";// *it = 10; 编译不通过}int array[]={1,2,3,4,5,6,7,8,9,0}; list<int>l(array, array +sizeof(array)/sizeof(array[0]));// 使用正向迭代器正向list中的元素// list<int>::iterator it = l.begin(); // C++98中语法auto it = l.begin();// C++11之后推荐写法while(it != l.end()){ cout <<*it <<" ";++it;} cout << endl;// 使用反向迭代器逆向打印list中的元素// list<int>::reverse_iterator rit = l.rbegin();auto rit = l.rbegin();while(rit != l.rend()){ cout <<*rit <<" ";++rit;}

1.2.3 list capacity

函数声明————接口说明
empty ——————检测list是否为空,是返回true,否则返回false
size ———————返回list中有效节点的个数

这两个接口直接使用就行,这里就不展示代码了

1.2.4 list element access

函数声明————接口说明
front ——————返回list的第一个节点中值的引用
back ——————返回list的最后一个节点中值的引用
这两个接口也是直接使用就行

1.2.5 list modifiers

函数声明————————接口说明
push_front ———————在list首元素前插入值为val的元素
pop_front ————————删除list中第一个元素
push_back ———————在list尾部插入值为val的元素
pop_back ————————删除list中最后一个元素
insert ——————————在list position 位置中插入值为val的元素
erase ——————————删除list position位置的元素
swap ——————————交换两个list中的元素
clear ——————————清空list中的有效元素
代码展示

int array[]={1,2,3}; list<int>L(array, array +sizeof(array)/sizeof(array[0]));// 在list的尾部插入4,头部插入0 L.push_back(4); L.push_front(0);PrintList(L);// 删除list尾部节点和头部节点 L.pop_back(); L.pop_front();PrintList(L);int array1[]={1,2,3}; list<int>L(array1, array1 +sizeof(array1)/sizeof(array1[0]));// 获取链表中第二个节点auto pos =++L.begin(); cout <<*pos << endl;// 在pos前插入值为4的元素 L.insert(pos,4);PrintList(L);// 在pos前插入5个值为5的元素 L.insert(pos,5,5);PrintList(L);// 在pos前插入[v.begin(), v.end)区间中的元素 vector<int> v{7,8,9}; L.insert(pos, v.begin(), v.end());PrintList(L);// 删除pos位置上的元素 L.erase(pos);PrintList(L);// 删除list中[begin, end)区间中的元素,即删除list中的所有元素 L.erase(L.begin(), L.end());PrintList(L);// 用数组来构造listint array1[]={1,2,3}; list<int>l1(array1, array1 +sizeof(array1)/sizeof(array1[0]));PrintList(l1);// 交换l1和l2中的元素 list<int> l2; l1.swap(l2);PrintList(l1);PrintList(l2);// 将l2中的元素清空 l2.clear(); cout << l2.size()<< endl;

list中还有一些操作,需要用到时大家可参阅list的文档说明

1.2.6 list的迭代器失效

前面说过,此处大家可将迭代器暂时理解成类似于指针迭代器失效即迭代器所指向的节点的无
,即该节点被删除了。因为list的底层结构为带头结点的双向循环链表,因此在list中进行插入
时是不会导致list的迭代器失效的,只有在删除时才会失效,并且失效的只是指向被删除节点的迭
代器,其他迭代器不会受到影响。

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);}}

上面有关list的使用和说明就到这里啦,下面我们来模拟实现一下list

二、list的模拟实现

前面在外面的vector的时候就已经模拟实现过,相信大家都有些熟悉了,其实list实现起来也差不多的,都是封装嘛
先用结构体封装一下迭代器

template<classT,classRef,classPtr>structlist_iterator{typedef list_node<T> Node;typedef list_iterator<T, Ref, Ptr> Self;// 这里模版的是const迭代器 会返回引用或者取地址其他模版参数 Node* _node;list_iterator(Node* node):_node(node){} Ref operator*(){return _node->_data;} Ptr operator->(){return&_node->_data;} 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;}booloperator!=(const Self& it){return _node != it._node;}booloperator==(const Self& it){return _node == it._node;}};

然后就是封装list,实现迭代器,各种增删改查函数,还有默认成员函数
这里我就不像vector一样一个一个赘述啦,相信模拟实现过string和vector之后其实这些写起来也就顺手啦

template<classT>classlist{typedef list_node<T> Node;public:typedef list_iterator<T, T&, T*> iterator;// 多个模版参数 与前面的迭代器封装相对应typedef list_iterator<T,const T&,const T*> const_iterator; iterator begin()// 迭代器{//return iterator(_head->_next); iterator it(_head->_next);return it;} iterator end(){returniterator(_head);} const_iterator begin()const{returnconst_iterator(_head->_next);} const_iterator end()const{returnconst_iterator(_head);}voidempty_init()// 初始化{ _head =new Node; _head->_next = _head; _head->_prev = _head; _size =0;}list()// 默认构造{empty_init();}list(initializer_list<T> lt)// c++11支持 { }构造{empty_init();for(auto& e : lt){push_back(e);}}// lt2(lt1)list(const list<T>& lt)// 拷贝构造{empty_init();for(auto& e : lt){push_back(e);}}// lt1 = lt3 list<T>&operator=(list<T> lt)// 赋值重载{swap(lt);return*this;}voidswap(list<T>& lt)// list内部的 swap{ std::swap(_head, lt._head); std::swap(_size, lt._size);}~list(){clear();delete _head; _head =nullptr;}voidclear()// 清楚函数{ iterator it =begin();while(it !=end()){ it =erase(it);}} size_t size()const{return _size;}voidpush_back(const T& x){/*Node* newnode = new Node(x); Node* tail = _head->_prev; newnode->_prev = tail; tail->_next = newnode; newnode->_next = _head; _head->_prev = newnode;*/insert(end(), x);}voidpush_front(const T& x){insert(begin(), x);}voidpop_front(){erase(begin());}voidpop_back(){erase(--end());}voidinsert(iterator pos,const T& x){ Node* cur = pos._node; Node* prev = cur->_prev; Node* newnode =newNode(x); prev->_next = newnode; newnode->_prev = prev; newnode->_next = cur; cur->_prev = newnode;++_size;} iterator erase(iterator pos){assert(pos !=end()); Node* cur = pos._node; Node* nextNode = cur->_next; Node* prevNode = cur->_prev; prevNode->_next = nextNode; nextNode->_prev = prevNode;delete cur;--_size;returniterator(nextNode);}private: Node* _head; size_t _size;};

经过几次的模拟实现stl,发现stl的容器大多都是类似的,有点期待后面的map和set
list 的模拟实现就到这里啦

list与vector的对比

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

在这里插入图片描述

总结

总结

C++ STL中的list是基于双向循环链表实现的序列容器,支持高效插入删除(O(1)时间复杂度),但随机访问效率较低。其核心特性包括:通过迭代器遍历元素(支持正向/反向迭代器)、插入操作不引发迭代器失效(删除仅影响被删节点迭代器)。模拟实现需封装节点结构体,设计泛型迭代器(重载++/--/*等操作),并实现深拷贝控制。与vector对比,list适合频繁增删场景,而vector更适合随机访问和内存连续需求。理解其底层实现有助于优化数据操作逻辑。
不要走开,小编持续更新中~~~~~
在这里插入图片描述

Read more

尚硅谷2025最新SpringCloud速通-操作步骤(详细)

尚硅谷2025最新SpringCloud速通-操作步骤(详细)

说明:本文是基于【雷丰阳老师:尚硅谷2025最新SpringCloud - 快速通关】进行实践操作,并对雷神的笔记做一个更详细的补充,供大家学习参考,一起加油! 视频地址:1、SpringCloud快速通关_教程简介_哔哩哔哩_bilibili 笔记链接:3. SpringCloud - 快速通关 资料:📎资料.zip(代码+课件+逻辑图) 本人代码:📎springcloud-demo.zip 用于测试API接口的工具:Apipost IDEA自动提示代码插件:通义灵码 目录 目录 springcloud简介 前期准备 建springcloud-demo项目 导依赖 建services模块 导入依赖 建service-order/product模块 nacos - 注册/配置中心 基础入门 注册中心

By Ne0inhk
开发兜不住?让数据库来兜底:金仓 SQL 防火墙的工程化实践

开发兜不住?让数据库来兜底:金仓 SQL 防火墙的工程化实践

开发兜不住?让数据库来兜底:金仓 SQL 防火墙的工程化实践 在真实的生产环境中,数据库安全从来不是“写完代码就结束”的问题,而是一个贯穿系统生命周期的持续对抗过程。哪怕你已经严格执行参数化查询、ORM 框架封装、输入校验等规范,仍然无法保证系统绝对无注入风险——遗留系统、动态 SQL、第三方组件、甚至临时脚本,都会成为潜在突破口。 这也是为什么越来越多企业开始将防线下沉到数据库层:既然应用层不可控,那就让数据库成为最后一道“强制执行的安全边界”。 本文结合 KingbaseES 的 SQL 防火墙机制,从原理、模式设计到性能表现,讲清楚它是如何在工程上解决 SQL 注入问题的。 一、SQL 注入的本质:语义劫持,而不是“字符串拼接问题” 很多人对 SQL 注入的理解还停留在“拼接字符串不安全”,但从数据库视角来看,本质其实是: 攻击者篡改了 SQL 的语义结构(

By Ne0inhk

Go语言的主流框架和解决超高并发的三高微服务框架对比分析

在Go语言生态中,主流的Web框架和应对“三高”(高并发、高可用、高可扩展)场景的微服务框架,经过多年的发展已经非常清晰。简单来说,Gin 是目前应用最广泛的通用Web框架,而像 go-zero、Kratos、KiteX 等则是专为“三高”微服务架构设计的“全家桶”式解决方案。 下面为你详细拆解这两大类框架。 一、主流通用Web框架:轻量、灵活、高性能 这类框架主要解决API构建、路由和中间件管理等Web层问题,是构建单体应用或微服务API层的良好基础。 Gin:目前的“默认选项”,性能高、社区庞大、中间件丰富,极易上手。如果你刚开始接触Go或项目需求明确,选择Gin会非常稳妥。 Fiber:受Express.js启发,语法对Node.js开发者很友好。它基于fasthttp构建,在性能基准测试中表现极为出色。适合追求极致性能、且不介意与标准库net/http不完全兼容的场景。 Echo:一个成熟且平衡的框架,

By Ne0inhk
必收藏!小白也能懂:Agent、Skills、MCP和A2A大模型架构完全指南

必收藏!小白也能懂:Agent、Skills、MCP和A2A大模型架构完全指南

文章详解AI Agent四大核心概念:Agent作为智能决策主体,Skills提供原子化能力封装,MCP实现标准化工具调用,A2A支持Agent间协作。这些技术共同构建了从单Agent自主执行到多Agent协同工作的完整技术栈,解决了智能体的自主性、模块化能力、工具调用和互操作等核心问题,助力开发者快速构建专业级AI应用。 一、Agent、Skills、MCP和A2A的核心概念总览 1、Agent (代理/智能体):自主决策与执行的“大脑”。 AI Agent是2026年AI生态的核心概念,是基于人工智能技术构建的、具备感知环境、理解信息、自主推理决策、自主规划与执行动作并持续与环境/其他主体交互,以自主达成预设或动态生成目标的数字智能实体。2026年的智能体不是在回答问题,而是在完成任务。其突破了传统问答式、生成式AI的能力边界,可像人类员工一样独立处理复杂综合性任务。它以大模型为核心引擎,整合规划、记忆、工具调用与行动执行四大能力,形成「感知 - 认知 - 决策 - 执行 - 反馈」的完整智能闭环,

By Ne0inhk