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

C/C++ list模拟

模拟准备

避免和库冲突,自己定义一个命名空间

namespace yx
{template<class T>struct ListNode{ListNode<T>* _next;ListNode<T>* _prev;T _data;};template<class T>class list{typedef ListNode<T> Node;public:private:Node* _head;};}

这里的ListNode类为什么用struct呢?

知识点:

        class:如果类里有公有,有私有,建议用

        struct:如果一个类,全部成员不做访问限定,全公有,建议用

因为下面的list类是要经常访问ListNode的,所以建议用struct

模拟实现

 模板ListNode的构造

封装节点的数据

template<class T>
struct ListNode
{ListNode<T>* _next;ListNode<T>* _prev;T _data;ListNode(const T& data):_next(nullptr), _prev(nullptr),_data(data){}
};

list的构造

void list()
{//哨兵位_head = new Node;_head->next = _head;_head->prev = _head;
}

定义一个哨兵位,自己指向自己。


 

push_back( )

尾插

void push_back(const T& x)
{Node* newnode = new Node(x);Node* tail == _head->prev;tail->_next = newnode;newnode->_prev - tail;newnode->_next = _head;
}

最后一个节点就是头节点的前一个

如果是一个空链表呢?满足条件么?

是可以的,这时候_head 和 tail都是哨兵位(head)

迭代器

说起迭代器,我们先考虑如何遍历链表吧。

我们是否可以像vector那样呢?

typedef Node* iterator;

当然是不可以的。

vector的空间是连续的,而list的空间不连续,那我们如何获取Node*呢?

定义迭代器类

我们可以定义一个类,把节点_node封装起来,来弥补空间不连续的缺陷,能像vector那样一样来进行访问。

template<class T>
class ListIterator
{typedef ListNode<T> Node;Node* _node;
};

template定义的模板参数只能供当前类或当前函数用

而且我们可以在这个类里重载想要的东西

使用时,我们就可以给每个类规范这个迭代器使用的名字,方便我们使用

typedef ListIterator<T> iterator;
template<class T>
class ListIterator
{typedef ListNode<T> Node;typedef ListIterator<T> self; // 返回自己Node* _node;self& operator++(){_node = _node->_next;return *this;//返回节点自己}T& operator*(){return _node->_data;//返回数据,数据类型为T}bool operator!=(const self& it){return _node != it._node;}};

iterator begin( ) 、 iterator end( )

我们实现了迭代器,我们来实现一下链表的头指针和尾指针吧

在list类里

iterator begin()
{//iterator it(_head->_next);//return it;//我们采用匿名对象return iterator(_head->_next);
}
iterator end()
{return iterator(_head;
}

测试通过

operator->( )

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

我们来测试一下下面的代码

struct pos
{int _row;int _col;pos(int row = 0, int col = 0):_row(row), _col(col){}
};void test2()
{list<pos> lt1;lt1.push_back(pos(100, 100));lt1.push_back(pos(200, 100));lt1.push_back(pos(300, 100));list<pos>::iterator it = lt1.begin();while (it != lt1.end()){cout << (*it)._row << ":" << (*it)._col << " ";cout << it->_row << ":" << it->_col << " ";++it;}cout << endl;}

第一种用的是对象.成员

第二种指针->成员

第一种比较好理解,it调用operator*(),返回data数据,也就是pos,然后pos._row,pos._col来访问

第二种比较绕,这里为了可读性,省略可一个->,但如果写两个的话不符合语法,于是

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

        第一个->是operator重载的,而第二个是原生指针的解引用

const迭代器

不能是普通迭代器前面+const修饰。

如:const iterator const 

const迭代器目标本身可以修改,指向的内容不能修改 , 

类似:const T* p

p可以修改,*p不能修改

因为T* 和 T&,这里我们可以写一个和迭代器一样的const迭代器类,但我们也可以类模板,传不同的参数,形成不同的类,让编译器帮我们实现。避免了写连个差不多重复的类,减少代码量

insert( )

//在pos前插入val
iterator insert(iterator pos, const T& val)
{Node* pNewNode = new Node(val);Node* pCur = pos._node;pNewNode->_prev = pCur->_prev;pNewNode->_next = pCur;pNewNode->_prev->_next = pNewNode;pCur->_prev = pNewNode;return iterator(pNewNode);
}

erase( )

//删除pos节点
iterator erase(iterator pos)
{Node* pDel = pos._node;Node* pRet = pDel->_next;pDel->_prev->_next = pDel->_next;pDel->_next->_prev = pDel->_prev;delete pDel;return iterator(pRet);
}

clear()

void clear()
{Node* cur = _head->_next;while (cur != _head){//从头开始删(从左向右)_head->_next = cur->_next;delete cur;cur = _head->_next;}_head->_next = _head->_prev = _head;
}

~list()

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

相关文章:

  • 谷歌优化指南:提升网站排名的关键要素与方法
  • ENSP实现防火墙区域策略与用户管理
  • 71.WEB渗透测试-信息收集- WAF、框架组件识别(11)
  • 迎接AI新时代:GPT-5的技术飞跃与未来展望
  • C++入门基础
  • 国密证书(gmssl)在Kylin Server V10下安装
  • bi项目笔记
  • ZooKeeper实现分布式锁
  • 浅析 VO、DTO、DO、PO 的概念
  • Oracle透明数据加密:数据泵文件导出
  • 5.SpringBoot核心源码-启动类源码分析
  • Redis 7.x 系列【23】哨兵模式
  • 进程信号
  • VINS-Fusion源码逐行解析:除单目+imu模式外的位姿初始化函数initFramePoseByPnP()及其内部函数
  • 科普文:Redis一问一答
  • 【159天】尚学堂高琪Java300集视频精华笔记(128)
  • Invalidate和postInvalidate的区别
  • Travix是如何部署应用程序到Kubernetes上的
  • Vue2 SSR 的优化之旅
  • windows下如何用phpstorm同步测试服务器
  • yii2中session跨域名的问题
  • 道格拉斯-普克 抽稀算法 附javascript实现
  • 关于使用markdown的方法(引自CSDN教程)
  • 计算机常识 - 收藏集 - 掘金
  • 突破自己的技术思维
  • 我感觉这是史上最牛的防sql注入方法类
  • 3月7日云栖精选夜读 | RSA 2019安全大会:企业资产管理成行业新风向标,云上安全占绝对优势 ...
  • 湖北分布式智能数据采集方法有哪些?
  • ​​快速排序(四)——挖坑法,前后指针法与非递归
  • # .NET Framework中使用命名管道进行进程间通信
  • #NOIP 2014# day.2 T2 寻找道路
  • #传输# #传输数据判断#
  • (~_~)
  • (八)Flask之app.route装饰器函数的参数
  • (代码示例)使用setTimeout来延迟加载JS脚本文件
  • (定时器/计数器)中断系统(详解与使用)
  • (二)原生js案例之数码时钟计时
  • (附程序)AD采集中的10种经典软件滤波程序优缺点分析
  • (附源码)apringboot计算机专业大学生就业指南 毕业设计061355
  • (六)库存超卖案例实战——使用mysql分布式锁解决“超卖”问题
  • (每日持续更新)jdk api之FileFilter基础、应用、实战
  • (原创)可支持最大高度的NestedScrollView
  • (转)关于多人操作数据的处理策略
  • (自用)gtest单元测试
  • .htaccess配置重写url引擎
  • .NET Framework .NET Core与 .NET 的区别
  • .net 开发怎么实现前后端分离_前后端分离:分离式开发和一体式发布
  • .net 受管制代码
  • .netcore如何运行环境安装到Linux服务器
  • .NetCore实践篇:分布式监控Zipkin持久化之殇
  • 。Net下Windows服务程序开发疑惑
  • @entity 不限字节长度的类型_一文读懂Redis常见对象类型的底层数据结构
  • [ Linux 长征路第五篇 ] make/Makefile Linux项目自动化创建工具
  • [000-002-01].数据库调优相关学习
  • [8] CUDA之向量点乘和矩阵乘法