模拟实现list

目录

  • list的实现结构
  • 节点的实现
  • 迭代器的实现
    • 第一个模板参数T
    • 第二个模板参数Ref
    • 第三个模板参数Ptr
  • 实现list中的接口函数
    • 插入和删除
    • 赋值重载和拷贝构造
    • 析构函数
  • 总结

list的实现结构

STL库中的list的结构是双向循环链表,所以我们这里也实现一个双向循环链表

我们这里采取带头双向循环链表

为了方便节点间的操作,可以将节点封装成一个结构体

list中的迭代器是双向迭代器,只支持++--操作,不支持+,-,所以不能用一个指针用作迭代器,所以还需要将迭代器封装成一个结构体,在结构体中对迭代器的一些操作进行实现


节点的实现

template<class T>struct __list_node{__list_node<T>* _next;__list_node<T>* _prev;T _val;__list_node(const T& val = T()):_next(nullptr),_prev(nullptr),_val(val){}};

主义,这是一个类模板,类名是__list_node,类型是__list_node<T>
所以2个指针_next_prev的类型是__list_node<T>,分别指向前一个节点和后一个节点

现在,我们就可以把头节点作在为ist类中的成员变量

节点的类型为__list_node<T>,比较麻烦,这里我们可以 typedef __list_node<T> Node

因为在类外我们不会直接用到Node,所以把typedef __list_node<T> Node放到private中就可以

同时实现构造函数,让_head_next指向自己,_prev也指向自己

template<class T >class list{typedef __list_node<T> Node;public:list(){_head = new Node;_head->_next = _head;_head->_prev = _head;}private:Node* _head;};

迭代器的实现

list的迭代器是双向迭代器,只支持++--操作,不支持+,-
所以不能只是简单的服用指针
要对指针进行封装,在结构体中实现++,--,*,->等操作

第一个模板参数T

list迭代器的底层其实还是Node*类型的指针

	template<class T>struct __list__iterator{typedef __list_node<T> Node;typedef __list__iterator<T> Iter;__list__iterator(Node* pnode):_pnode(pnode){}Node* _pnode;};

因为总使用__list_node<T>__list__iterator<T>比较麻烦,所以typedef __list_node<T> Node; typedef __list__iterator<T> Iter;

++操作实际上的底层是当前迭代器指向当前指向节点的下一个节点
--操作实际上的底层是当前迭代器指向当前指向节点的前一个节点

所以下面就可以实习++,--操作

Iter& operator++()
{_pnode = _pnode->_next;return *this;
}Iter operator++(int)
{Iter tmp(*this);_pnode = _pnode->_next;return tmp;
}Iter& operator--()
{_pnode = _pnode->_prev;return *this;
}Iter operator--(int)
{Iter tmp(*this);_pnode = _pnode->_prev;return tmp;
}

解引用操作也就是返回迭代器指向节点中的值

T& operator*()
{return _pnode->_val;
}T* operator->()
{return &_pnode->_val;
}

!===就是比较迭代器下面的指针

bool operator!=(const Iter it)
{return _pnode != it._pnode;
}bool operator==(const Iter it)
{return _pnode == it._pnode;
}

接下来在list中实现begin()end()函数
__list__iterator<T>麻烦,typedef成iterator

typedef __list__iterator<T> iterator;

我们需要在类外使用到iterator,所以这句typedef要放到public中

list的结构如下:
在这里插入图片描述
因为begin()返回的是指向第一个有效节点的迭代器,所以_head->_next就是第一个有效节点

iterator begin()
{return iterator(_head->_next);
}

end()返回的是最后一个节点的下一个位置,这里最后一个节点的下一个位置就是_head
在这里插入图片描述

iterator end()
{return iterator(_head);
}

第二个模板参数Ref

现在有一个问题,就是如何实现const_iterator,在前面iterator的基础上,我们可以在封装一个const_iterator的结构体,其中的返回值改为const T&const T*

但是再封装一个,就会导致代码冗余

这里我们引入第二个模板参数class Ref
这个参数是用来接收返回引用的类型的可以是T&也可以是const T&

所以这时的*重载返回值就是Ref

Ref operator*()
{return _pnode->_val;
}

第三个模板参数Ptr

同理,对于->重载的返回类型是指针
引入第三个模板参数Ptr

它是用来接收T*const T*

Ptr operator->()
{return &_pnode->_val;
}

现在就可以在list中实现cbegincend1

首先还是将typedefiteratorconst_iterator

typedef __list__iterator<T, T&, T*> iterator;
typedef __list__iterator<T, const T&, const T*> const_iterator;
const_iterator begin()const
{return const_iterator(_head->_next);
}const_iterator end()const
{return const_iterator(_head);
}

实现list中的接口函数

插入和删除

这里的inserterase的实现和C语言中实现链表的操作一样,比较容易

这里唯一需要注意的是:insert函数返回插入新节点的迭代器,erase函数返回删除节点后,删除节点下一个节点位置的迭代器

iterator insert(iterator it, const T& val)
{Node* newnode = new Node(val);Node* cur = it._pnode;Node* prev = it._pnode->_prev;newnode->_next = cur;cur->_prev = newnode;prev->_next = newnode;newnode->_prev = prev;_size++;return newnode;
}iterator erase(iterator it)
{assert(it != _head);//需要检查一下it是否是头节点位置的迭代器,头节点不能删除Node* del = it._pnode;Node* prev = it._pnode->_prev;Node* nex = it._pnode->_next;prev->_next = nex;nex->_prev = prev;_size--;delete del;return nex;
}

实现了inserterase后,通过复用这两个函数,就可以实现push_back,push_front,pop_back,pop_front函数了

void push_back(const T& val)
{insert(end(), val);
}void push_front(const T& val)
{insert(begin(), val);
}void pop_back()
{erase(_head->_prev);
}void pop_front()
{erase(_head->_next);
}

赋值重载和拷贝构造

赋值重载的实现,可以使用现代写法,和在vector中赋值重载的写法类似,使用swap

void swap(list<T> lt)
{std::swap(_head, lt._head);std::swap(_size, lt._size);
}//赋值重载现代写法
list<T> operator=(list<T> lt)	
{swap(lt);return *this;
}

拷贝构造,我们先构造出一个list,然后用push_back向里面插入节点

//拷贝构造
list(const list<T>& lt)
{_head = new Node;_head->_next = _head;_head->_prev = _head;_size = 0;for (auto& e : lt){push_back(e);}}

这里有一点:
库中复制拷贝和运算符重载中参数列表中形参类型不是list<T>,而是list类型没有指定后面的模板参数
在这里插入图片描述
在这里插入图片描述


析构函数

析构函数需要清理空间,对于链表来说,我们不能只清理头节点
我们需要清理掉有有效节点后,最后清理掉头节点

这里我们定义一个清理掉有有效节点的函数
使用erase+循环清理节点

void clear()
{iterator it = begin();while (it != end()){it = erase(it);}_size = 0;
}

然后在销毁函数中调用clear函数,再清理掉头节点

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

总结

此时,list的模拟实现就基本完成了

  • list的模拟实现中,有难度的地方就是把指针封装成迭代器,在封装中实现以及限制了迭代器的作用
  • 对于复杂类型,我们可以typedef出一个简单的别名,对于这个别名,如果要在类外使用就定义在public里,如果不允许在类外使用,就定义在private中

完整代码:

namespace my_list
{template<class T>struct __list_node{__list_node<T>* _next;__list_node<T>* _prev;T _val;__list_node(const T& val = T()):_next(nullptr), _prev(nullptr), _val(val){}};//typedef __list__iterator<T,T&,T*> iterator;//typedef __list__iterator<T, const T&, const T*> const_iterator;template<class T, class Ref, class Ptr>struct __list__iterator{typedef __list_node<T> Node;typedef __list__iterator<T, Ref, Ptr> Iter;__list__iterator(Node* pnode):_pnode(pnode){}Node* _pnode;bool operator!=(const Iter it){return _pnode != it._pnode;}bool operator==(const Iter it){return _pnode == it._pnode;}Iter& operator++(){_pnode = _pnode->_next;return *this;}Iter operator++(int){Iter tmp(*this);_pnode = _pnode->_next;return tmp;}Iter& operator--(){_pnode = _pnode->_prev;return *this;}Iter operator--(int){Iter tmp(*this);_pnode = _pnode->_prev;return tmp;}Ref operator*(){return _pnode->_val;}Ptr operator->(){return &_pnode->_val;}};template<class T >class list{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 end(){return iterator(_head);}const_iterator begin()const{return const_iterator(_head->_next);}const_iterator end()const{return const_iterator(_head);}void empty_init(){_head = new Node;_head->_next = _head;_head->_prev = _head;_size = 0;}list(){empty_init();}list(size_t n, const T& val = T()){empty_init();for (int i = 0; i < n; i++){push_back(val);}}~list(){clear();delete _head;_head = nullptr;}//拷贝构造list(const list<T>& lt)//list(const list& lt)   //库中的拷贝构造是这么定义的,list类型没有指定后面的模板参数{empty_init();for (auto& e : lt){push_back(e);}}void swap(list<T> lt){std::swap(_head, lt._head);std::swap(_size, lt._size);}//赋值重载现代写法list<T> operator=(list<T> lt)//list operator=(list lt)  //库中的赋值重载是这么定义的,list类型没有指定后面的模板参数{swap(lt);return *this;}size_t size() const{return _size;}bool empty() const{if (_head->_next == _head || _head->_prev == _head)return true;elsereturn false;}iterator insert(iterator it, const T& val){Node* newnode = new Node(val);Node* cur = it._pnode;Node* prev = it._pnode->_prev;newnode->_next = cur;cur->_prev = newnode;prev->_next = newnode;newnode->_prev = prev;_size++;return newnode;}iterator erase(iterator it){assert(it != _head);//需要检查一下it是否是头节点位置的迭代器,头节点不能删除Node* del = it._pnode;Node* prev = it._pnode->_prev;Node* nex = it._pnode->_next;prev->_next = nex;nex->_prev = prev;_size--;delete del;return nex;}void push_back(const T& val){insert(end(), val);}void push_front(const T& val){insert(begin(), val);}void pop_back(){erase(_head->_prev);}void pop_front(){erase(_head->_next);}void print(){iterator it = begin();while (it != end()){std::cout << *it << " ";it++;}std::cout << std::endl;}void clear(){iterator it = begin();while (it != end()){it = erase(it);//经过erase后的迭代器会失效,为了让循环继续下去,用it接收erase返回的迭代器(删除位置迭代器的下一个位置的迭代器)}_size = 0;}private:Node* _head;size_t _size; //加一个_size成员变量,如果不设置_size,在实现size()函数时就需要遍历list,时间复杂度为O(n)};}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/98275.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

万里路,咫尺间:汽车与芯片的智能之遇

目前阶段&#xff0c;汽车产业有两个最闪耀的关键词&#xff0c;就是智能与低碳。 在践行双碳目标与产业智能化的大背景下&#xff0c;汽车已经成为了能源技术、交通技术、先进制造以及通信、数字化、智能化技术的融合体。汽车的产品形态与产业生态都在发生着前所未有的巨大变革…

Python 快速输出接下来几个月的月末日期

# pip install arrow 安装库&#xff08;若已安装 可忽略&#xff09; import arrowEnd_of_month_date [] # 月末日期 for i in range(100):if arrow.now().shift(daysi).format(YYYY-MM-DD)[-2:] 01: # 若日期为 1 号 则返回前一天的日期End_of_month_date.append(arrow.now…

MySQL——MySQL的基础操作部分

使用命令行登录 mysql -u root -p 直接敲击回车后输入密码即可&#xff1a; 当看到出现“mysql>“的符号之后&#xff0c;就表示已经进入到了&#xff2d;&#xff59;&#xff33;&#xff31;&#xff2c;系统中&#xff0c;就可以输入&#xff2d;&#xff59;&#xf…

Java中的网络编程------基于Socket的TCP编程和基于UDP的网络编程,netstat指令

Socket 在Java中&#xff0c;Socket是一种用于网络通信的编程接口&#xff0c;它允许不同计算机之间的程序进行数据交换和通信。Socket使得网络应用程序能够通过TCP或UDP协议在不同主机之间建立连接、发送数据和接收数据。以下是Socket的基本介绍&#xff1a; Socket类型&…

将本地jar打包到本地maven仓库或maven私服仓库中

将本地jar包打包到本地的maven仓库中的命令&#xff1a; mvn install:install-file -DgroupIdtebie.applib.api -DartifactIdapiclient -Dversion1.0-SNAPSHOT -Dfile本地jar路径 -Dpackagingjar说明&#xff1a; DgroupId pom中的<groupId></groupId> Dartifact…

用半天时间从零开始复习前端之html

目录 前言 科班生的标配&#xff1a;半天听完一门标记型语言 准备工作 webstorm2022 webstrom 第一个html页面 body h系列标签 行标签和块标签 列表标签 表格标签&#xff08;另起一篇&#xff09; 万能的input 1.快速生成多个标签 2.同时选中多个 前言 科班生的标…

GIT实战篇,教你如何使用GIT可视化工具

系列文章目录 手把手教你安装Git&#xff0c;萌新迈向专业的必备一步 GIT命令只会抄却不理解&#xff1f;看完原理才能事半功倍&#xff01; 快速上手GIT命令&#xff0c;现学也能登堂入室 GIT实战篇&#xff0c;教你如何使用GIT可视化工具 系列文章目录一、GIT有哪些常用工具…

工服穿戴检测联动门禁开关算法

工服穿戴检测联动门禁开关算法通过yolov8深度学习框架模型&#xff0c;工服穿戴检测联动门禁开关算法能够准确识别和检测作业人员是否按照规定进行工服着装&#xff0c;只有当人员合规着装时&#xff0c;算法会发送开关量信号给门禁设备&#xff0c;使门禁自动打开。YOLO的结构…

什么是Web组件(Web Components)?它们有哪些主要特点?

聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ Web 组件&#xff08;Web Components&#xff09;⭐ Web 组件的主要特点1. 自定义元素&#xff08;Custom Elements&#xff09;2. Shadow DOM3. HTML 模板4. 封装性和重用性5. 生态系统6. 跨框架兼容性 ⭐ 写在最后 ⭐ 专栏简介 前端入门…

SpringBoot整合MQ

1.创建工程并引入依赖 <!-- 添加rocketmq的启动器--><dependency><groupId>org.apache.rocketmq</groupId><artifactId>rocketmq-spring-boot-starter</artifactId><version>2.1.1</version></dependency>2.编写…

sql server 备份到网络共享

场景&#xff1a;sql server服务器A将数据库备份文件备份到服务器B 1&#xff09;服务器B创建共享目录 这里我将 D:\ProDbBak 共享&#xff0c;并且Everyone完全控制 2&#xff09;sql server服务器A能够访问服务器B共享目录&#xff0c;并且能完全控制 3&#xff09;修改服务…

【马蹄集】第二十四周——高精度计算专题

高精度计算专题 目录 MT2191 整数大小比较MT2192 AB problemMT2193 A-B problemMT2194 大斐列MT2195 升级版斐波那契数列MT2196 2的N次幂 MT2191 整数大小比较 难度&#xff1a;黄金    时间限制&#xff1a;1秒    占用内存&#xff1a;128M 题目描述 给出两个正整数&…