当前位置: 首页 > news >正文

C++奇迹之旅:深度解析list的模拟实现

请添加图片描述

文章目录

  • 📝前言
  • 🌠list节点
    • 🌉list
  • 🌠迭代器的创建
    • 🌉const迭代器
  • 🌠代码
  • 🚩总结


📝前言

🌠list节点

我们先建立一个列表里的节点类listnode,用来构造list的节点使用:

// 这个宏定义用于禁用编译器的安全警告。
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <assert.h>
using namespace std;namespace own
{// 定义一个模板结构 listnode,代表链表中的节点template<typename T>struct listnode{// 指向前一个节点的指针listnode<T>* _prev;// 指向下一个节点的指针listnode<T>* _next;// 节点中存储的数据T _data;// 构造函数,初始化链表节点listnode(const T& data = T()): _prev(nullptr) // 前一个节点指针初始化为 nullptr, _next(nullptr) // 下一个节点指针初始化为 nullptr, _data(data)    // 节点中存储的数据初始化为传入的值(或默认值){}};
}

🌉list

在这里插入图片描述
list主要框架:

	template<typename T>class list{typedef listnode<T> Node;public:typedef ListIterator<T> iterator;typedef Listconst_Iterator<T> const_Iterator;list(){_head = new Node();_head->_prev = _head;_head->_next = _head;}iterator begin(){//iterator it(head->next);//return itreturn iterator(_head->_next);}iterator end(){return iterator(_head);}const_Iterator begin() const{//iterator it(head->next);//return itreturn const_Iterator(_head->_next);}const_Iterator end() const{return const_Iterator(_head);}private:Node* _head;};

begin使用迭代器iterator返回第一个数据,end返回最后一个数据的下一个位置,也就是头结点。

void test_list01()
{list<int> first;for (int i = 0; i < 4; i++){first.push_back(i);}//ListIterator<int> it = first.begin();list<int>::iterator it = first.begin();while (it != first.end()){cout << *it << " ";++it;}cout << endl;for (auto e : first){cout << e << " ";}cout << endl;
}

在这里插入图片描述
为了方便使用iterator,需要重新typedef命名:

typedef ListIterator<T> iterator;
typedef Listconst_Iterator<T> const_Iterator;

🌠迭代器的创建

在这里插入图片描述
在这里插入图片描述
我们使用模拟实现vector时,迭代器类型使用的是T*,因为vector底层是数组,地址连续,但是list不能使用T*,因为指针不能直接++,或–;也不能直接使用Node进行++或–,因此Node不符合遍历的行为,需要Listlterator类封装Node*,再通过重载运算符控制他的行为,具体原因也有以下:

  1. 内存布局

    • vector 中,元素是连续存储在内存中的,因此可以使用指针(如 T*)进行简单的算术运算(如 ++--)来访问相邻元素。
    • list 中,元素是分散存储的,每个节点通过指针连接到前一个和下一个节点。节点之间的内存地址不连续,因此不能简单地通过指针算术来访问相邻节点。
  2. 迭代器的设计

    • 对于 vector,迭代器通常是一个指向数组元素的指针(如 T*),可以直接进行指针运算。
    • 对于 list,迭代器需要封装一个指向节点的指针(如 Node*),并提供自定义的 ++-- 操作符来遍历链表。这是因为在链表中,节点之间的关系是通过指针而不是通过内存地址的连续性来维护的。
  3. 迭代器的有效性

    • 在链表中,插入和删除操作可能会导致迭代器失效。使用 Node* 作为迭代器类型时,删除节点后,指向该节点的迭代器将变得无效。因此,链表的迭代器需要在操作后返回一个新的有效迭代器(如在 erase 方法中返回下一个节点的迭代器)。
template<typename T>
struct ListIterator
{typedef listnode<T> Node;typedef ListIterator<T> self;Node* _node;ListIterator(Node* node):_node(node){}bool operator!=(const self& it){return _node != it._node;}bool operator==(const self& it){return _node = it._node;}};

我们先实现list的简单构造函数,接下来是迭代器++和–

//++it
self& operator++()
{_node = _node->_next;return *this;
}//it++
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;
}

那么迭代器怎么访问数据的两种方法:
对于pos类:

struct Pos
{int _row;int _col;Pos(int row = 0, int col = 0):_row(row),_col(col){}
};

先把数据插入的多种方法:

void test_list2()
{list<Pos> lt1;A aa1(100,100);A aa2 = {100,100};lt1.push_back(aa1);lt1.push_back(aa2);lt1.push_back({100,100})lt1.push_back(Pos(100, 100));
}

我们使用其中一种方法插入数据就行:

void test_list2()
{list<Pos> lt1;lt1.push_back(Pos(100, 100));lt1.push_back(Pos(200, 200));lt1.push_back(Pos(300, 300));list<Pos>::iterator it = lt1.begin();while (it != lt1.end()){cout << (*it)._row << ":" << (*it)._col << endl;++it;}cout << endl;
}

这里数据是struct公有的,解引用得到的可以通过.访问符进行访问

cout << (*it)._row << ":" << (*it)._col << endl;

这里访问方式就是指针解引用用.访问符进行取数据:

A* ptr = &aa1;
(*ptr)._a1;

在这里插入图片描述
第二种:
还有使用->访问:

cout << it->_row << ":" << it->_col << endl;

但是这样需要重载operator->运算符

T* operator->()
{return &_node->_data;
}

返回的是_data的地址
在这里插入图片描述
实际上它是有两个->的,第一个是oeprator()->,第二个是->

cout << it.operator->()->_row << ":" << it.operator->()->_col << endl;

这里隐藏了一个箭头,一个是重载,一个是原生指针的访问操作
在你提供的 test_list02 函数中,确实存在一个关于箭头操作符(->)的重载和原生指针访问的混合使用。让我们详细分析一下这个情况。

🌉const迭代器

typedef Listconst_Iterator<const T> const_Iterator;

对于这个cons_itertator直接这样加是不行的,无法编译成功,const不能调用非const成员函数

我们增加const版本的迭代器

typedef Listconst_Iterator<T> const_Iterator;
const_Iterator begin() const
{//iterator it(head->next);//return itreturn const_Iterator(_head->_next);
}const_Iterator end() const
{return const_Iterator(_head);
}

这里实现const迭代器呢?
第一种:单独实现一个类,修改正常版本的迭代器:

template<typename T>
class Listconst_Iterator
{typedef listnode<T> Node;typedef Listconst_Iterator<T> self;
public:Node* _node;Listconst_Iterator(Node* node):_node(node){}//++itself& operator++(){_node = _node->_next;return *this;}//it++self operator++(int){self tmp(*this);_node = _node->_next;return tmp;}self& operator--(){_node = _node->_prev;return _node;}self operator--(int){self tmp(*this);_node = _node->_prev;return tmp;}const T* operator->(){return &_node->_data;}const T& operator*(){return _node->_data;}bool operator!=(const self& it){return _node != it._node;}bool operator==(const self& it){return _node = it._node;}};

我们目的是不修改指向的值,只需要在这两个函数前面加上const即可:

const T* operator->()
{return &_node->_data;
}
const T& operator*()
{return _node->_data;
}

在这里插入图片描述
第二种:合并两个迭代器

template<typename T,class ptr,class ref>
struct ListIterator
{typedef listnode<T> Node;typedef ListIterator<T, ptr, ref> self;Node* _node;ListIterator(Node* node):_node(node){}ptr operator->(){return &_node->_data;}ref operator*(){return _node->_data;}bool operator!=(const self& it){return _node != it._node;}bool operator==(const self& it){return _node = it._node;}};
  1. 模板定义
template<typename T, class ptr, class ref>
struct ListIterator
  • ListIterator 是一个模板结构体,接受三个模板参数:
    • T:表示链表中存储的数据类型。
    • ptr:表示指向 T 类型的指针类型(通常是 T*)。
    • ref:表示对 T 类型的引用类型(通常是 T&)。
  1. 类型别名
typedef listnode<T> Node;
typedef ListIterator<T, ptr, ref> self;
  • Node 是一个类型别名,表示链表节点的类型,即 listnode<T>
  • self 是一个类型别名,表示当前迭代器类型 ListIterator<T, ptr, ref>,用于在成员函数中引用自身类型。
ptr operator->()
{return &_node->_data;
}
  • 重载了箭头操作符 operator->(),使得可以通过迭代器访问节点中的数据。
  • 返回 _node 指向的节点的 _data 成员的地址,允许使用 it-> 语法来访问数据。
ref operator*()
{return _node->_data;
}
  • 重载了解引用操作符 operator*(),使得可以通过迭代器直接访问节点中的数据。
  • 返回 _node 指向的节点的 _data 成员,允许使用 *it 语法来访问数据。

最后我们需要在使用list里重新定义:

	template<typename T>class list{typedef listnode<T> Node;public://typedef ListIterator<T> iterator;//typedef Listconst_Iterator<T> const_Iterator;typedef ListIterator<T, T*, T&> iterator;typedef ListIterator<T, const T*, const T&> const_Iterator;list(){_head = new Node();_head->_prev = _head;_head->_next = _head;}......}

搞定了这些就是插入的删除的一些操作了

insert()
在这里插入图片描述

insert在这里不会出现迭代器失效,我们可以按照库里的写法进行模仿:

//没有iterator失效
iterator insert(iterator pos, const T& x)
{Node* cur = pos._node;Node* newnode = new Node(x);Node* prev = cur->_prev;prev->_next = newnode;newnode->_prev = prev;newnode->_next = cur;cur->_prev = newnode;return iterator(newnode);
}

erase

//erase 后pos失效了,pos指向的节点就被释放了
iterator erase(iterator pos)
{Node* cur = pos._node;Node* prev = cur->_prev;Node* next = cur->_next;prev->_next = next;next->_prev = prev;delete cur;return iterator(next);
}

erase()函数完成后,it所指向的节点已被删除,所以it无效,在下一次使用it时,必须先给其赋值erase删除返回的是下一个位置的迭代器

🌠代码

list.h

# define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <assert.h>using namespace std;namespace own
{template<typename T>struct listnode{listnode<T>* _prev;listnode<T>* _next;T _data;listnode(const T& data = T()):_prev(nullptr), _next(nullptr), _data(data){}};template<typename T,class ptr,class ref>struct ListIterator{typedef listnode<T> Node;typedef ListIterator<T, ptr, ref> self;Node* _node;ListIterator(Node* node):_node(node){}//++itself& operator++(){_node = _node->_next;return *this;}//it++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;}ptr operator->(){return &_node->_data;}ref operator*(){return _node->_data;}bool operator!=(const self& it){return _node != it._node;}bool operator==(const self& it){return _node = it._node;}};//template<typename T>//class Listconst_Iterator//{//	typedef listnode<T> Node;//	typedef Listconst_Iterator<T> self;//public://	Node* _node;//	Listconst_Iterator(Node* node)//		:_node(node)//	{}//	//++it//	self& operator++()//	{//		_node = _node->_next;//		return *this;//	}//	//it++//	self operator++(int)//	{//		self tmp(*this);//		_node = _node->_next;//		return tmp;//	}//	self& operator--()//	{//		_node = _node->_prev;//		return _node;//	}//	self operator--(int)//	{//		self tmp(*this);//		_node = _node->_prev;//		return tmp;//	}//	const T* operator->()//	{//		return &_node->_data;//	}//	const T& operator*()//	{//		return _node->_data;//	}//	bool operator!=(const self& it)//	{//		return _node != it._node;//	}//	bool operator==(const self& it)//	{//		return _node = it._node;//	}//};template<typename T>class list{typedef listnode<T> Node;public://typedef ListIterator<T> iterator;//typedef Listconst_Iterator<T> const_Iterator;typedef ListIterator<T, T*, T&> iterator;typedef ListIterator<T, const T*, const T&> const_Iterator;list(){_head = new Node();_head->_prev = _head;_head->_next = _head;}iterator begin(){//iterator it(head->next);//return itreturn iterator(_head->_next);}iterator end(){return iterator(_head);}const_Iterator begin() const{//iterator it(head->next);//return itreturn const_Iterator(_head->_next);}const_Iterator end() const{return const_Iterator(_head);}void push_back(const T& val = T()){/*Node* newnode = new Node(val);Node* tail = _head->_prev;tail->_next = newnode;newnode->_prev = tail;newnode->_next = _head;_head->_prev = newnode;*/insert(end(), val);}void pop_back(){erase(--end());}void push_front(const T& val = T()){insert(begin(), val);}void pop_front(){erase(begin());}//没有iterator失效iterator insert(iterator pos, const T& x){Node* cur = pos._node;Node* newnode = new Node(x);Node* prev = cur->_prev;prev->_next = newnode;newnode->_prev = prev;newnode->_next = cur;cur->_prev = newnode;return iterator(newnode);}//erase 后pos失效了,pos指向的节点就被释放了iterator erase(iterator pos){Node* cur = pos._node;Node* prev = cur->_prev;Node* next = cur->_next;prev->_next = next;next->_prev = prev;delete cur;return iterator(next);}private:Node* _head;};void test_list01(){list<int> first;for (int i = 0; i < 4; i++){first.push_back(i);}//ListIterator<int> it = first.begin();list<int>::iterator it = first.begin();while (it != first.end()){cout << *it << " ";++it;}cout << endl;for (auto e : first){cout << e << " ";}cout << endl;}struct pos{int _row;int _col;pos(int row = 0, int col = 0):_row(row), _col(col){}};void test_list02(){list<pos> It1;It1.push_back(pos(100, 200));It1.push_back(pos(300, 400));It1.push_back(pos(500, 600));list<pos>::iterator it = It1.begin();while (it != It1.end()){//cout << (*it)._row << ":" << (*it)._col << endl;// 为了可读性,省略了一个->//cout << it->_row << ":" << it->_col << endl;//cout << it->->_row << ":" << it->->_col << endl;cout << it.operator->()->_row << ":" << it.operator->()->_col << endl;++it;}cout << endl;}void Fun(const list<int>& lt){list<int>::const_Iterator it = lt.begin();while (it != lt.end()){cout << *it << " ";++it;}cout << endl;}void test_list03(){list<int> It2;It2.push_back(1);It2.push_back(2);It2.push_back(3);Fun(It2);}void test_list04(){list<int> It3;It3.push_back(1);It3.push_back(2);It3.push_back(3);It3.insert(It3.begin(), 100);It3.push_back(1);It3.push_back(2);It3.push_back(3);It3.erase(++It3.begin());list<int>::iterator it = It3.begin();while (it != It3.end()){cout << *it << " ";++it;}cout << endl;It3.pop_back();list<int>::iterator it2 = It3.begin();while (it2 != It3.end()){cout << *it2 << " ";++it2;}cout << endl;It3.push_front(200);list<int>::iterator it3 = It3.begin();while (it3 != It3.end()){cout << *it3 << " ";++it3;}cout << endl;It3.pop_front();list<int>::iterator it4 = It3.begin();while (it4 != It3.end()){cout << *it4 << " ";++it4;}}
}

🚩总结

请添加图片描述

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 【时时三省】(C语言基础)指针进阶6qsort函数的使用
  • BCC软译码和硬译码之间的性能差别
  • CAN协议通信 学习笔记
  • Linux启动流程和内核管理
  • 使用python导出Excel表格中的lua配置
  • 【网络安全 | 虚拟机】VMware Workstation Pro下载安装使用教程(免费版)
  • C语言深度复习【数组和指针】
  • 滚雪球学MyBatis-Plus(02):环境准备
  • python-word添加标题,段落,文字块
  • C++ 计算日期到天数转换(牛客网)
  • 基于SpringBoot+Vue+MySQL的宠物寄养服务管理系统
  • Java throw和throws有什么区别?
  • 将工程内的组件 集成并发布到私有仓库以及后续联动运行(热启动)
  • Hibernate 批量插入速度慢的原因和解决方法
  • 六、Selenium操作指南(二)
  • 【刷算法】求1+2+3+...+n
  • E-HPC支持多队列管理和自动伸缩
  • EOS是什么
  • ES6 学习笔记(一)let,const和解构赋值
  • Fundebug计费标准解释:事件数是如何定义的?
  • JavaScript 基本功--面试宝典
  • javascript面向对象之创建对象
  • MySQL用户中的%到底包不包括localhost?
  • Node项目之评分系统(二)- 数据库设计
  • Windows Containers 大冒险: 容器网络
  • 服务器从安装到部署全过程(二)
  • 欢迎参加第二届中国游戏开发者大会
  • 基于HAProxy的高性能缓存服务器nuster
  • 前端 CSS : 5# 纯 CSS 实现24小时超市
  • 使用 Node.js 的 nodemailer 模块发送邮件(支持 QQ、163 等、支持附件)
  • 微服务框架lagom
  • 温故知新之javascript面向对象
  • [Shell 脚本] 备份网站文件至OSS服务(纯shell脚本无sdk) ...
  • 如何在 Intellij IDEA 更高效地将应用部署到容器服务 Kubernetes ...
  • 树莓派用上kodexplorer也能玩成私有网盘
  • ​MySQL主从复制一致性检测
  • #php的pecl工具#
  • #pragma data_seg 共享数据区(转)
  • (Oracle)SQL优化技巧(一):分页查询
  • (Repost) Getting Genode with TrustZone on the i.MX
  • (附源码)node.js知识分享网站 毕业设计 202038
  • (蓝桥杯每日一题)平方末尾及补充(常用的字符串函数功能)
  • (六) ES6 新特性 —— 迭代器(iterator)
  • (免费领源码)python#django#mysql校园校园宿舍管理系统84831-计算机毕业设计项目选题推荐
  • (一)基于IDEA的JAVA基础10
  • (转)IIS6 ASP 0251超过响应缓冲区限制错误的解决方法
  • .NET delegate 委托 、 Event 事件,接口回调
  • .Net Framework 4.x 程序到底运行在哪个 CLR 版本之上
  • .net 调用海康SDK以及常见的坑解释
  • .NET/C# 使窗口永不激活(No Activate 永不获得焦点)
  • .NET:自动将请求参数绑定到ASPX、ASHX和MVC(菜鸟必看)
  • .net遍历html中全部的中文,ASP.NET中遍历页面的所有button控件
  • .NET中两种OCR方式对比
  • //TODO 注释的作用
  • @RequestBody与@ResponseBody的使用