透过源码看本质:list 的模拟实现与核心原理

透过源码看本质:list 的模拟实现与核心原理

一、STL源码

定义节点

在这里插入图片描述

迭代器

在这里插入图片描述

list定义

在这里插入图片描述


在这里插入图片描述

成员函数

在这里插入图片描述


在这里插入图片描述

初始化

在这里插入图片描述

从内存池中获取空间

在这里插入图片描述

创建节点

在这里插入图片描述

定位new 初始化

在这里插入图片描述

销毁节点

在这里插入图片描述


在这里插入图片描述

二、模拟实现 list 容器

1. list成员变量

//定义节点template<classT>structlist_node{ list_node<T>* _prev;//指向前驱节点 list_node<T>* _next;//指向后继结点 T val;//存储值//默认构造函数list_node(const T& x =T()):_prev(nullptr),_next(nullptr),val(x){}};//容器listtemplate<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;private: Node* _head; size_t _size;//list_node _head;};

2. 构造函数

//初始化voidempty_init(){//带头节点 _head =new Node; _head->_prev = _head; _head->_next = _head; _size =0;}//默认构造函数list(){empty_init();}

list是一个带头的双向循环链表,因此,构造时创建一个带头的节点

3. insert

voidinsert(const iterator& pos,const T& x)//void insert(iterator pos, const T& x){ Node* cur = pos._node; Node* prev = cur->_prev; Node* newnode =newNode(x); newnode->_prev = prev; prev->_next = newnode; newnode->_next = cur; cur->_prev = newnode;++_size;}

list容器数据结构之带头双向循环链表这篇文章的原理是一样的,只不过进行了封装而已。看不懂细节的,可以移步这篇文章。list容器,重点讲解迭代器部分

4. push_back

voidpush_back(const T& x){ Node* newnode =newNode(x); Node* tail = _head->_prev; tail->_next = newnode; newnode->_prev = tail; newnode->_next = _head; _head->_prev = newnode;++_size;}

其实,push_back就是尾插,可以复用 insert 函数。这样去实现。

voidpush_back(const T& x){insert(end(), x);}

这样是不是简单多了,end返回的就是list容器中的末尾后的一个位置(即头结点),而插入节点是在该位置之前插入的

5. 拷贝构造

//lt2(lt1)list(list<T>& lt){empty_init();for(constauto& e : lt){push_back(e);}}

拷贝构造也需要先初始化 list 对象,在调用 push_back 逐个插入元素

6. 初始化列表构造

list(initializer_list<T> lt){empty_init();for(constauto& e : lt){push_back(e);}}

逐个访问初始化列表中的元素,进行插入

7. operator=

voidswap(list<T>& lt){ std::swap(_head, lt._head); std::swap(_size, lt._size);}//lt3=lt2//拷贝构造+交换 list<T>&operator=(list<T> lt){swap(lt);return*this;}

通过拷贝构造出一个局部对象,在进行资源的交换就完成赋值重载了

8. erase

iterator erase(iterator pos){assert(pos !=end()); Node* cur = pos._node; Node* prev = cur->_prev; Node* next = cur->_next; prev->_next = next; next->_prev = prev;delete cur;--_size;//隐式类型转换returniterator(next);}

请看数据结构之带头双向循环链表这篇文章。

9. pop_back,push_front,pop_front

voidpop_back(){erase(--end());}voidpush_front(const T& x){insert(begin(), x);}voidpop_front(){erase(begin());}

直接复用insert,erase函数即可

10. clear

voidclear(){ iterator it =begin();while(it !=end()){ it =erase(it);}}

清空 list 中的所有元素,从 list 中的 begin 位置的节点开始删除

11. 析构函数

~list(){clear();delete _head; _head =nullptr; _size =0;}

清空 list 容器中的所有节点,再删除头结点,更新_size

12. size

size_t size()const{return _size;}

13. 迭代器

iterator begin(){//_head->_next是一个指向list_node节点的指针//iterator是一个list_iterator的类//隐式类型转换,借助构造函数//返回的是iterator的临时对象//return _head->_next;returniterator(_head->_next);} iterator end(){//隐式类型转换//return _head;returniterator(_head);} const_iterator begin()const{returnconst_iterator(_head->_next);} const_iterator end()const{//隐式类型转换//return _head;returnconst_iterator(_head);}

以前使用库里面的迭代器,都是直接获取迭代器,该迭代器指向某个元素的这块空间,通过解引用就可以访问到该元素。那么,在 list 这里,可以直接用原生指针Node*(list_node<T>*)作为迭代器吗?答案是不可以的

因为如果用原生指针Node*(内置类型)作为迭代器,那么解引用能访问到元素,但是使用迭代器的方式就会与平时不一样,需要手动访问数据,而且原生指针是一个内置类型,要如何通过++操作访问下一个节点呢?基于以上原因,因此,不能用原生指针作为迭代器

那要如何处理呢?

我们可以对原生指针进行封装,使之成为一个自定义类型,这样不就可以通过解引用操作,调用运算符重载函数了,访问到数据了吗!其次,自定义类型在++操作的时候,也可以通过运算符重载指向下一个Node节点

具体实现如下。

template<classT,classRef,classPtr>structlist_iterator{typedef list_node<T> Node;typedef list_iterator<T, Ref, Ptr> Self;//内置类型,拷贝构造浅拷贝即可 Node* _node;//构造函数list_iterator(Node* node):_node(node){} Ref operator*(){return _node->val;} Ptr operator->(){return&_node->val;} Self&operator++(){ _node = _node->_next;return*this;} Self operator++(int){//浅拷贝,默认拷贝构造函数就可以用 list_iterator<T>tmp(*this); _node = _node->_next;//局部变量,不能用引用返回return tmp;} Self&operator--(){ _node = _node->_prev;return*this;} Self operator--(int){ list_iterator<T>tmp(*this); _node = _node->_prev;return tmp;}booloperator!=(const Self& it){return _node != it._node;}booloperator==(const Self& it){return _node == it._node;}};

可以看到,这里不仅有模板参数T,还多了两个模板参数Ref 和 Ptr。实现迭代器的时候,不仅实现了普通迭代器,还实现了 const 版本的迭代器,如果不增加这两个模板参数,我们就还需要写一个类来封装Node*指针,而且,这两个类的代码基本是一样的,在返回值,参数类型上会有所区别,代码会非常的冗余。所以,添加两个模板参数就可以很好的解决这个问题了

三、完整代码实现

. list.h

#pragmaonce#include<iostream>#include<assert.h>usingnamespace std;namespace LC {template<classT>structlist_node{ list_node<T>* _prev; list_node<T>* _next; T val;//默认构造函数list_node(const T& x =T()):_prev(nullptr),_next(nullptr),val(x){}};template<classT,classRef,classPtr>structlist_iterator{typedef list_node<T> Node;typedef list_iterator<T, Ref, Ptr> Self;//内置类型,拷贝构造浅拷贝即可 Node* _node;//构造函数list_iterator(Node* node):_node(node){} Ref operator*(){return _node->val;} Ptr operator->(){return&_node->val;} Self&operator++(){ _node = _node->_next;return*this;} Self operator++(int){//浅拷贝,默认拷贝构造函数就可以用 list_iterator<T>tmp(*this); _node = _node->_next;//局部变量,不能用引用返回return tmp;} Self&operator--(){ _node = _node->_prev;return*this;} Self operator--(int){ list_iterator<T>tmp(*this); _node = _node->_prev;return tmp;}booloperator!=(const Self& it){return _node != it._node;}booloperator==(const Self& it){return _node == it._node;}};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(){//_head->_next是一个指向list_node节点的指针//iterator是一个list_iterator的类//隐式类型转换,借助构造函数//返回的是iterator的临时对象//return _head->_next;returniterator(_head->_next);} iterator end(){//隐式类型转换//return _head;returniterator(_head);} const_iterator begin()const{returnconst_iterator(_head->_next);} const_iterator end()const{//隐式类型转换//return _head;returnconst_iterator(_head);}voidempty_init(){ _head =new Node; _head->_prev = _head; _head->_next = _head; _size =0;}//默认构造函数list(){empty_init();}//lt2(lt1)list(list<T>& lt){empty_init();for(constauto& e : lt){push_back(e);}}list(initializer_list<T> lt){empty_init();for(constauto& e : lt){push_back(e);}}voidswap(list<T>& lt){ std::swap(_head, lt._head); std::swap(_size, lt._size);}//lt3=lt2//拷贝构造+交换 list<T>&operator=(list<T> lt){swap(lt);return*this;} size_t size()const{return _size;}voidpush_back(const T& x){ Node* newnode =newNode(x); Node* tail = _head->_prev; tail->_next = newnode; newnode->_prev = tail; newnode->_next = _head; _head->_prev = newnode;++_size;//insert(end(), x);}//在pos位置之前插入voidinsert(const iterator& pos,const T& x)//void insert(iterator pos, const T& x){ Node* cur = pos._node; Node* prev = cur->_prev; Node* newnode =newNode(x); newnode->_prev = prev; prev->_next = newnode; newnode->_next = cur; cur->_prev = newnode;++_size;} iterator erase(iterator pos){assert(pos !=end()); Node* cur = pos._node; Node* prev = cur->_prev; Node* next = cur->_next; prev->_next = next; next->_prev = prev;delete cur;--_size;//隐式类型转换returniterator(next);}voidpop_back(){erase(--end());}voidpush_front(const T& x){insert(begin(), x);}voidpop_front(){erase(begin());}voidclear(){ iterator it =begin();while(it !=end()){ it =erase(it);}}~list(){clear();delete _head; _head =nullptr; _size =0;}private: Node* _head; size_t _size;//list_node _head;};}

. test.cc

#define_CRT_SECURE_NO_WARNINGS#include"list.h"structA{int _a1;int _a2;A(int a1 =1,int a2 =2):_a1(a1),_a2(a2){}};voidtestlist1(){ LC::list<int> l1; l1.push_back(1); l1.push_back(2); l1.push_back(3); l1.push_back(4); LC::list<int>::iterator it = l1.begin();//l1.end()返回的是临时变量,形参必须是const&while(it != l1.end()){ cout <<*it <<" ";++it;} cout << endl; l1.insert(l1.begin(),5); l1.insert(l1.end(),6); it = l1.begin();while(it != l1.end()){ cout <<*it <<" ";++it;} cout << endl; l1.erase(l1.begin()); it = l1.begin();while(it != l1.end()){ cout <<*it <<" ";++it;} cout << endl;}voidtestlist2(){ LC::list<int> l1; l1.push_back(1); l1.push_back(2); l1.push_back(3); l1.push_back(4); LC::list<int>l2(l1); LC::list<int>::iterator it = l2.begin();while(it != l2.end()){ cout <<*it <<" ";++it;} cout << endl; cout << l2.size()<< endl; LC::list<int> l3; l3.push_back(10); l3.push_back(20); l3.push_back(30); l3.push_back(40); l3.push_back(50); l2 = l3; LC::list<int>::iterator it1 = l2.begin();while(it1 != l2.end()){ cout <<*it1 <<" ";++it1;} cout << endl; cout << l2.size()<< endl;//LC::list<double> l4 = { 100,200,300,400 }; LC::list<double> l4 ={1.1,2.2,3.3,4.4}; LC::list<double>::iterator it2 = l4.begin();while(it2 != l4.end()){ cout <<*it2 <<" ";++it2;} cout << endl; cout << l4.size()<< endl; LC::list<A> l5; l5.push_back({1,1}); l5.push_back({2,2}); l5.push_back({3,3}); l5.push_back({4,4}); LC::list<A>::iterator it3 = l5.begin();while(it3 != l5.end()){ cout << it3->_a1 <<" "<< it3->_a2 << endl;++it3;} cout << endl; cout << l5.size()<< endl;}intmain(){testlist2();return0;}

今天的文章分享到此结束,感谢观看。

Read more

养龙虾-------【多openclaw 对接飞书多应用】---多个大龙虾机器人群聊

🚀 MiniMax Token Plan 惊喜上线!新增语音、音乐、视频和图片生成权益。邀请好友享双重好礼,助力开发体验! 好友立享 9折 专属优惠 + Builder 权益,你赢返利 + 社区特权! 👉 立即参与:https://platform.minimaxi.com/subscribe/token-plan?code=2NMAwoNLlZ&source=link 最近玩了下大龙虾,对接飞书后玩的不亦乐乎,妥妥滴私人助理。但是也萌发一个想法,多个机器人可以自己聊天吗?那会不会把世界给聊翻了。于是我马上搜寻各个配置方式,却是找到了可以配置多个机器人得群聊方式。 1.首先创建多个应用添加机器人,分别和部署得多个openclaw系统对接具体对接参考我写的【 养龙虾-------【openclaw 对接飞书、钉钉、微信 】—移动AI助理】 2.手工拉群并添加机器人: 3.把群id配置进各个龙虾配置文件里面 接下来就可以群聊了

By Ne0inhk
【OpenHarmony】鸿蒙Flutter智能家居应用开发实战指南

【OpenHarmony】鸿蒙Flutter智能家居应用开发实战指南

鸿蒙Flutter智能家居应用开发实战指南 概述 智能家居是鸿蒙全场景生态的重要应用场景。本文讲解如何基于鸿蒙Flutter框架,开发一套完整的智能家居应用,实现设备发现、控制、场景联动、语音交互等核心功能。 欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net 系统架构设计 整体架构图 ┌────────────────────────────────────────────────────────────┐ │ 用户交互层 (Flutter) │ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │ │ 设备控制面板 │ │ 场景编排 │ │ 语音交互 │ │ │ └─────────────┘ └─────────────┘ └─────────────┘ │ └───────────────────────┬────────────────────────────────────┘ │ RPC/事件总线 ┌────────────────────

By Ne0inhk
nginx - 实现域名跳转的几种方式

nginx - 实现域名跳转的几种方式

Nginx 实现域名跳转的几种方式 文章目录 * Nginx 实现域名跳转的几种方式 * 1. 301 永久重定向(推荐 SEO 场景) * 2. 302 临时重定向(推荐活动页/短链场景) * 3. 强制 HTTPS 跳转 * 4. 去掉或强制 `www` * 去掉 `www` → 跳到裸域名 * 强制加 `www` * 5. 正则匹配更复杂的跳转 * 6. 总结 * 7. 常见问题 * 1. 301 和 302 的区别 * 2. `return 302 https://event.new.com$request_uri;` 是否是固定写法 * 举个例子

By Ne0inhk
大数据新视界 -- Hive 数据仓库设计模式:星型与雪花型架构(2 - 16 - 3)

大数据新视界 -- Hive 数据仓库设计模式:星型与雪花型架构(2 - 16 - 3)

💖💖💖亲爱的朋友们,热烈欢迎你们来到 青云交的博客!能与你们在此邂逅,我满心欢喜,深感无比荣幸。在这个瞬息万变的时代,我们每个人都在苦苦追寻一处能让心灵安然栖息的港湾。而 我的博客,正是这样一个温暖美好的所在。在这里,你们不仅能够收获既富有趣味又极为实用的内容知识,还可以毫无拘束地畅所欲言,尽情分享自己独特的见解。我真诚地期待着你们的到来,愿我们能在这片小小的天地里共同成长,共同进步。💖💖💖 本博客的精华专栏: 1. 大数据新视界专栏系列:聚焦大数据,展技术应用,推动进步拓展新视野。 2. Java 大厂面试专栏系列:提供大厂面试的相关技巧和经验,助力求职。 3. Python 魅力之旅:探索数据与智能的奥秘专栏系列:走进 Python 的精彩天地,感受数据处理与智能应用的独特魅力。 4. Java 性能优化传奇之旅:铸就编程巅峰之路:如一把神奇钥匙,深度开启 JVM 等关键领域之门。丰富案例似璀璨繁星,引领你踏上编程巅峰的壮丽征程。 5. Java 虚拟机(

By Ne0inhk