【C++】STL——list的模拟实现、构造函数、迭代器类的实现、运算符重载、增删查改

文章目录

  • 1.模拟实现list
    • 1.1构造函数
    • 1.2迭代器类的实现
    • 1.3运算符重载
    • 1.4增删查改

1.模拟实现list

list使用文章

在这里插入图片描述

1.1构造函数

在这里插入图片描述

析构函数

在这里插入图片描述

  在定义了一个类模板list时。我们让该类模板包含了一个内部结构体_list_node,用于表示链表的节点。该结构体包含了指向前一个节点和后一个节点的指针以及节点的值。在list中保存了链表的头节点指针和链表长度大小。

namespace my_list
{	//用类模板定义一个list结点template<class T>struct _list_node{//list结点的构造函数,T()作为缺省值,当未转参时调用_list_node(const T& val = T()):_next(nullptr), _prev(nullptr), _val(val){}//list_node的成员函数,list为带头双向循环链表_list_node* _prev;_list_node* _next;T _val;};//用类模拟实现listtemplate<class T>class list{private:typedef _list_node<T> Node;public://list的构造函数,初始化头节点指针list(){_head = new Node;_head->_prev = _head;_head->_next = _head;_size = 0;}//析构函数~list(){iterator it = begin();while (it != end()){it = erase(it);}_size = 0;delete _head;_head = nullptr;}private://成员函数为list的头节点指针,和链表长度Node* _head;size_t _size;};
}

1.2迭代器类的实现

在这里插入图片描述
在这里插入图片描述

  因为list的底层实现和之前的vector和string不同,vector和string是数组和顺序表,而list底层是链表。而对于数组和顺序表,我们可以直接使用指针来实现其迭代器,但是对于list类型就无法使用指针来实现它的迭代器,因为迭代器++无法访问到他的下一个节点。所以我们这里建立一个迭代器的结构体,用来封装node指针,来实现list的专属迭代器

  这段代码定义了一个名为__list_iterator的迭代器结构体,用于遍历链表。该结构体模板有三个模板参数:T表示链表中节点的数据类型,Ref表示引用类型T&,Ptr表示指针类型 T* _node:指向链表节点的指针。构造函数__list_iterator(Node* node):用于初始化迭代器对象,将指针node赋值给_node。通过_node就可以实现各种操作符重载。

  定义了一个迭代器结构体,用于在链表中进行遍历和操作。通过重载运算符,可以使用迭代器对象像指针一样访问链表中的元素。

//使用类进行封装Node*,来实现list的专属迭代器
//list迭代器和一些内置类型迭代器不一样,++无法准确访问到下一个位置
//typedef __list_iterator<T, T&> iterator;
//typedef __list_iterator<T, const T&> iterator;
template<class T, class Ref, class Ptr>
struct __list_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){self tmp(*this);_node = _node->_prev;return tmp;}//重载!=bool operator!=(const self& it) const{return _node != it._node;}//重载==bool operator==(const self& it) const{return _node == it._node;}
};

  通过建立一个新的结构体来重载迭代器,我们即可实现list专属迭代器对其链表进行++、- -等操作。

typedef __list_iterator<T, T&, T*> iterator;
typedef __list_iterator<T, const T&, const T*> const_iterator;//list迭代器实现
iterator begin()
{//隐式类型转换,内置类型转换为自定义类型,实现迭代器功能//return _head->_next;return iterator(_head->_next);
}iterator end()
{//return _head;return iterator(_head);
}const_iterator begin() const
{//return _head->_next;return const_iterator(_head->_next);
}const_iterator end() const
{//return _head;return const_iterator(_head);
}

1.3运算符重载

  迭代器的运算符重载在上面的代码中,已经都基本实现了。

在这里插入图片描述

  赋值运算符重载函数operator=首先创建了一个临时的链表对象lt,并将传入的链表对象的副本赋值给lt。然后,调用swap函数,将当前链表对象和临时链表对象进行交换。这样做的原因是为了实现异常安全的赋值操作。通过将传入的链表对象的副本赋值给临时链表对象,可以确保在交换过程中不会影响到传入的链表对象。 最后,返回当前链表对象的引用。

  通过这样的实现逻辑,可以实现链表对象之间的赋值操作,并且保证在异常发生时不会破坏数据的完整性。

//交换
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)
{swap(lt);return *this;
}

1.4增删查改

push_back

在这里插入图片描述
  和带头双向循环链表的尾插一样。首先,创建一个新的链表节点newnode,节点的值为传入的参数x。然后,获取到链表的尾指针tail,即头节点的前一个节点。

  接着,将新节点newnode插入到链表的尾部。首先,将新节点的前驱指针指向尾节点tail,将尾节点的后继指针指向新节点。这样就将新节点连接到了链表的尾部。

  然后,将新节点的后继指针指向头节点,将头节点的前驱指针指向新节点。这样就将新节点连接到了链表的头部。

//尾插
void push_back(const T& x)
{//创建一个新的链表结点,且获取到链表的尾指针Node* newnode = new Node(x);Node* tail = _head->_prev;//连接尾结点newnode->_prev = tail;tail->_next = newnode;//连接头节点_head->_prev = newnode;newnode->_next = _head;++_size;
}

insert

在这里插入图片描述

  和上面的逻辑类似。

//在pos位置之前插入
iterator insert(iterator pos, const T& x)
{Node* cur = pos._node;Node* prev = cur->_prev;Node* newnode = new Node(x);prev->_next = newnode;newnode->_next = cur;cur->_prev = newnode;newnode->_prev = prev;++_size;return newnode;
}

erase

在这里插入图片描述
  首先,通过断言判断传入的迭代器pos不等于链表的末尾迭代器。如果等于末尾迭代器,表示要删除的节点不存在,会导致错误。然后,根据传入的迭代器pos获取到要删除的节点cur,以及其前驱节点prev和后继节点next。

  接着,将前驱节点的后继指针指向后继节点,将后继节点的前驱指针指向前驱节点。这样就将要删除的节点从链表中断开。然后,释放要删除的节点的内存。还要将链表的大小减1。

  为了避免迭代器失效,返回下一个节点的指针作为新的迭代器。这样可以保证在删除节点后,仍然可以使用返回的迭代器进行遍历和操作。

//删除
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;//为了避免迭代器失效,返回下一个结点指针return next;
}


完整实现

#pragma once#include<assert.h>namespace my_list
{	//用类模板定义一个list结点template<class T>struct _list_node{//list结点的构造函数,T()作为缺省值,当未转参时调用_list_node(const T& val = T()):_next(nullptr), _prev(nullptr), _val(val){}//list_node的成员函数,list为带头双向循环链表_list_node* _prev;_list_node* _next;T _val;};//使用类进行封装Node*,来实现list的专属迭代器//list迭代器和一些内置类型迭代器不一样,++无法准确访问到下一个位置//typedef __list_iterator<T, T&> iterator;//typedef __list_iterator<T, const T&> iterator;template<class T, class Ref, class Ptr>struct __list_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){self tmp(*this);_node = _node->_prev;return tmp;}//重载!=bool operator!=(const self& it) const{return _node != it._node;}//重载==bool operator==(const self& it) const{return _node == it._node;}};//实现const类型迭代器/*template<class T>struct __list_const_iterator{typedef list_node<T> Node;Node* _node;__list_const_iterator(Node* node):_node(node){}const T& operator*(){return _node->_val;}__list_const_iterator<T>& operator++(){_node = _node->_next;return *this;}__list_const_iterator<T> operator++(int){__list_const_iterator<T> tmp(*this);_node = _node->_next;return tmp;}bool operator!=(const __list_const_iterator<T>& it){return _node != it._node;}bool operator==(const __list_const_iterator<T>& it){return _node == it._node;}};*///用类模拟实现listtemplate<class T>class list{private:typedef _list_node<T> Node;public:typedef __list_iterator<T, T&, T*> iterator;typedef __list_iterator<T, const T&, const T*> const_iterator;//list迭代器实现iterator begin(){//隐式类型转换,内置类型转换为自定义类型,实现迭代器功能//return _head->_next;return iterator(_head->_next);}iterator end(){//return _head;return iterator(_head);}const_iterator begin() const{//return _head->_next;return const_iterator(_head->_next);}const_iterator end() const{//return _head;return const_iterator(_head);}//初始化void empty_init(){_head = new Node;_head->_prev = _head;_head->_next = _head;_size = 0;}//list的构造函数,初始化头节点指针list(){empty_init();}//拷贝构造list(const list<T>& lt)//list(const list& lt){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){swap(lt);return *this;}//析构函数~list(){clear();delete _head;_head = nullptr;}//清理void clear(){iterator it = begin();while (it != end()){it = erase(it);}_size = 0;}//尾插void push_back(const T& x){//创建一个新的链表结点,且获取到链表的尾指针Node* newnode = new Node(x);Node* tail = _head->_prev;//连接尾结点newnode->_prev = tail;tail->_next = newnode;//连接头节点_head->_prev = newnode;newnode->_next = _head;++_size;//insert(end(), x);}//头插void push_front(const T& x){insert(begin(), x);}//尾删void pop_back(){erase(--end());}//头删void pop_front(){erase(begin());}//在pos位置之前插入iterator insert(iterator pos, const T& x){Node* cur = pos._node;Node* prev = cur->_prev;Node* newnode = new Node(x);prev->_next = newnode;newnode->_next = cur;cur->_prev = newnode;newnode->_prev = prev;++_size;return newnode;}//删除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;//为了避免迭代器失效,返回下一个结点指针return next;}//返回sizesize_t size(){/*size_t sz = 0;iterator it = begin();while (it != end()){++sz;++it;}return sz;*/return _size;}private://成员函数为list的头节点指针,和链表长度Node* _head;size_t _size;};//打印函数void print(const list<int>& lt){list<int>::const_iterator it = lt.begin();while (it != lt.end()){// (*it) += 1;cout << *it << " ";++it;}cout << endl;}
}


测试代码

#define _CRT_SECURE_NO_WARNINGS 1#include<iostream>
#include<list>
using namespace std;#include"list.h"void test_list1()
{//list<int> lt;//lt.push_back(1);//lt.push_back(2);//lt.push_back(3);//lt.push_back(4);//for (auto e : lt)//{//	cout << e << " ";//}//cout << endl;my_list::list<int> lt;lt.push_back(1);lt.push_back(2);lt.push_back(3);lt.push_back(4);my_list::list<int>::iterator it = lt.begin();while (it != lt.end()){cout << *it << " ";++it;}cout << endl;auto itt = lt.begin();while (itt != lt.end()){++(*itt);++itt;}for (auto e: lt){cout << e << " ";}cout << endl;
}struct A
{A(int a1 = 0, int a2 = 0):_a1(a1), _a2(a2){}int _a1;int _a2;
};void test_list2()
{my_list::list<A> lt;lt.push_back(A(1, 1));lt.push_back(A(2, 2));lt.push_back(A(3, 3));lt.push_back(A(4, 4));auto it = lt.begin();while (it != lt.end()){//cout << *(it)._a1 << " " << *(it)._a2 <<endl;cout << it->_a1 << " " << it->_a2 << endl;++it;}cout << endl;
}void test_list3()
{my_list::list<int> lt;lt.push_back(1);lt.push_back(2);lt.push_back(3);lt.push_back(4);for (auto e : lt){cout << e << " ";}cout << endl;lt.push_front(5);lt.push_front(6);print(lt);lt.pop_front();lt.pop_back();print(lt);lt.clear();lt.push_back(10);lt.push_back(20);lt.push_back(30);lt.push_back(40);print(lt);cout << lt.size() << endl;}int main()
{//test_list1();//test_list2();test_list3();return 0;
}

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

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

相关文章

深度学习入门 ---- 张量(Tensor)

文章目录 张量张量在深度学习领域的定义张量的基本属性使用PyTorch安装PyTorch查看安装版本 创建张量常用函数四种创建张量的方式和区别 四则运算 张量 张量在深度学习领域的定义 张量&#xff08;tensor&#xff09;是多维数组&#xff0c;目的是把向量、矩阵推向更高的维度。…

uniapp 微信小程序:v-model双向绑定问题(自定义 props 名无效)

uniapp 微信小程序&#xff1a;v-model双向绑定问题&#xff08;自定义 props 名无效&#xff09; 前言问题双向绑定示例使用 v-model使用 v-bind v-on使用 sync 修饰符 参考资料 前言 VUE中父子组件传递数据的基本套路&#xff1a; 父传子 props子传父 this.$emit(事件名, …

如何安装、部署、启动Jenkins

一、测试环境 Linux系统 Centos 7 二、安装步骤&#xff1a; 1、安装jdk 我安装的是jdk8&#xff0c;此处就不多说了&#xff0c;自己百度哈&#xff0c;很简单 2、安装jenkins 首先依次执行如下三个命令&#xff1a; 2.1、导入镜像&#xff1a; [rootcentos7 ~]# sudo …

Python(五十八)什么是字典

❤️ 专栏简介&#xff1a;本专栏记录了我个人从零开始学习Python编程的过程。在这个专栏中&#xff0c;我将分享我在学习Python的过程中的学习笔记、学习路线以及各个知识点。 ☀️ 专栏适用人群 &#xff1a;本专栏适用于希望学习Python编程的初学者和有一定编程基础的人。无…

学习C#编写上位机的基础知识和入门步骤:

00001. 掌握C#编程语言基础和.NET框架的使用。 00002. 学习WinForm窗体应用程序开发技术&#xff0c;包括控件的使用和事件驱动编程。 00003. 熟悉基本的数据结构和算法知识&#xff0c;如链表、栈、队列等。 00004. 理解串口通信协议和通信方法&#xff0c;用于与底层硬件设…

elb 直接配置到后端服务器组

出现上图报错的原因是&#xff0c;前面elb配置了https证书&#xff0c;后端的nginx也配置了证书&#xff0c;导致冲突。 需要修改后端的nginx配置文件&#xff0c;将证书配置注释掉。 如果出现健康检查异常&#xff0c;需要在对应服务器的安全组上配置elb所在的网段的访问权限…

CTFSHOW php 特性

web89 数组绕过正则 include("flag.php"); highlight_file(__FILE__);if(isset($_GET[num])){$num $_GET[num]; get numif(preg_match("/[0-9]/", $num)){ 是数字 就输出 nodie("no no no!");}if(intval($num)){ 如果是存在整数 输出 flagecho …

formatter的用法,深拷贝, Object.assign 方法实战。

1. :formatter的用法 :formatter 接受一个函数作为参数&#xff0c;这个函数有三个参数&#xff1a;row&#xff0c;column 和 cellValue。row 是当前行的数据&#xff0c;column 是当前列的数据&#xff0c;cellValue 是当前单元格的值。 <el-table-column prop"SYS…

sql入门基础-2

Dml语句 对数据的增删改查 关键字 Insert增 Update删 Delete改 添加数据 给指定字段添加数据 Insert into 表明 (字段名1&#xff0c;字段名2) values&#xff08;值1&#xff0c;值2&#xff09;; 给全部字段添加数据--(根据位置对应添加到字段下) Insert into 表名 values…

基于ESP8266+网络调试助手点灯实验

文章目录 ESP8266串口wifi模块简介实验准备硬件接线程序下载注意事项总结 ESP8266串口wifi模块 简介 ESP8266 是一种低成本、高性能的 Wi-Fi 模块&#xff0c;内置了 TCP/IP 协议栈&#xff0c;它可以作为单独的无线网络控制器&#xff0c;或者与其他微控制器进行串口通信。它…

STM SPI学习

SPI介绍 SPI&#xff1a;串行外设设备接口&#xff08;Serial Peripheral Interface&#xff09;&#xff0c;是一种高速的&#xff0c;全双工&#xff0c;同步通信总线。 IIC总线与SPI总线对比 全双工&#xff1a;同一时刻既能接收数据&#xff0c;也能发送数据。 CS&…

中国最赚钱公司出炉

7月25日&#xff0c;2023年《财富》中国500强排行榜正式发布。国家电网以5300亿美元的营收位居榜首&#xff0c;中国石油和中国石化分列第二和第三。工商银行则成为最赚钱的公司。 图片来源&#xff1a;财富FORTUNE 1中国500强TOP10 数据显示&#xff0c;今年500家上榜的中国…

Linux 用户和权限

一、root 用户 root 用户(超级管理员) 无论是windows、Macos、Linux均采用多用户的管理模式进行权限管理。在Linux系统中&#xff0c;拥有最大权限的账户名为&#xff1a;root (超级管理员)。 root用户拥有最大的系统操作权限&#xff0c;而普通用户在许多地方的权限是受限的。…

Keepalived 在CentOS 7安装并配置监听MySQL双主

keepalived安装 MySQL双主配置请看这里&#xff1a;https://tongyao.blog.csdn.net/article/details/132016200?spm1001.2014.3001.5502 128、129两台服务器安装步骤相同&#xff0c;配置文件不同&#xff0c;下面有介绍。 1.安装相关依赖包&#xff0c;并下载keepalived安…

Java课题笔记~ MyBatis入门

一、ORM框架 当今企业级应用的开发环境中&#xff0c;对象和关系数据是业务实体的两种表现形式。业务实体在内存中表现为对象&#xff0c;在数据库中变现为关系数据。当采用面向对象的方法编写程序时&#xff0c;一旦需要访问数据库&#xff0c;就需要回到关系数据的访问方式&…

Django Rest_Framework(一)

1. Web应用模式 在开发Web应用中&#xff0c;有两种应用模式&#xff1a; 前后端不分离[客户端看到的内容和所有界面效果都是由服务端提供出来的。] 前后端分离【把前端的界面效果(html&#xff0c;css&#xff0c;js分离到另一个服务端或另一个目录下&#xff0c;python服务…

Redis的键空间监听功能

文章目录 Redis 键空间通知一、keyspace介绍二、事件通知配置三、不同命令生成的事件四、客户端测试五、Springboot整合Redis键空间监听5.1 方式一5.2 方式二 Redis 键空间通知 一、keyspace介绍 keyspace&#xff08;键空间通知&#xff09;针对指定key发生的一切改动&#…

Stable Diffusion AI绘画初学者指南【概述、云端环境搭建】

概述、云端环境搭建 Stable Diffusion 是什么、能干啥&#xff1f; 是一种基于深度学习的图像处理技术&#xff0c;可以生成高质量的图像。它可以在不需要真实图像的情况下&#xff0c;通过文字描述来生成逼真的图像。 可以对图像进行修复、超分辨率转换&#xff0c;将低分辨…

【计算机网络】传输层协议 -- TCP协议

文章目录 1. TCP协议的引入2. TCP协议的特点3. TCP协议格式3.1 序号与确认序号3.2 发送缓冲区与接收缓冲区3.3 窗口大小3.4 六个标志位 4. 确认应答机制5. 超时重传机制6. 连接管理机制6.1 三次握手6.2 四次挥手 7. 流量控制8. 滑动窗口9. 拥塞控制10. 延迟应答11. 捎带应答12.…

数据库访问中间件--springdata-jpa的基本使用

二、单表SQL操作-使用关键字拼凑方法 回顾 public interface UserRepository extends JpaRepository<User,Integer> {User findByUsernameLike(String username); }GetMapping("/user/username/{username}")public Object findUserByUsername(PathVariable S…