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

C++:list使用以及模拟实现

list使用以及模拟实现

    • list介绍
    • list常用接口
      • 1.构造
      • 2.迭代器
      • 3.容量
      • 4.访问数据
      • 5.增删查改
      • 6.迭代器失效
    • list模拟实现
      • 1.迭代器的实现
      • 2.完整代码

list介绍

  1. list是一个类模板,加<类型>实例化才是具体的类
  2. list是可以在任意位置进行插入和删除的序列式容器。
  3. list的底层是双向循环链表结构,链表中每个元素存储在互不相关的独立节点中,在节点中通过指针指向其前一个元素和后一个元素。
  4. 与其他序列式容器相比,list最大的缺陷是不支持任意位置的随机访问,比如访问第六个节点,必须从已知节点迭代到该节点。

双向链表图解:
双向链表图解



list常用接口

1.构造

函数功能
list (size_type n, const value_type& val = value_type())构造的list中包含n个值为val的节点
list()构造空的list
list (const list& x)拷贝构造
list (InputIterator first, InputIterator last)迭代器区间初始化
(模板,可以传入其它容器的迭代器区间)

2.迭代器

函数功能
begin()加end()获取第一个数据位置的iterator/const_iterator,获取最后一个数据的下一个位置的iterator/const_iterator
rbegin()加rend()反向迭代器,获取最后一个数据位置的reverse_iterator,获取第一个数据前一个位置的reverse_iterator

3.容量

函数功能
size()获取有效数据个数
empty()判断是否为空(size为0为空,返回true)

4.访问数据

函数功能
front()取到头节点数据的引用
back()返回尾节点数据的引用

5.增删查改

函数功能
push_front
(const value_type& val)
头插数据val
push_back
(const value_type& val)
尾删数据val
pop_front()头删
pop_back()尾删
insert (iterator position, const value_type& val)在position位置中插入值为val的元素
erase (iterator position)删除position位置的元素
swap(list& x)交换两个list
clear()清空有效数据

6.迭代器失效

       迭代失效即迭代器所指向的节点的无效,即该节点被删除了。 因为list的底层结构为带头结点的双向循环链表,因此在list中进行插入时是不会导致list的迭代器失效的,只有在删除时才会失效,并且失效的只是指向被删除节点的迭代器,其他迭代器不会受到影响。

#include<iostream>
#include<list>
using namespace std;//错误代码演示
int main()
{int array[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };list<int> l(array, array + sizeof(array) / sizeof(array[0]));auto it = l.begin();while (it != l.end()){// erase()函数执行后,it所指向的节点已被删除//因此it无效,在下一次使用it时,必须先给其赋值it = l.erase(it); //只需要去掉++it,这里修改成it = erase(it)即可++it;}return 0;
}



list模拟实现

1.迭代器的实现

      有别于vector,list的迭代器并不是一个原生的指针,因为使用者需要得到的是节点中的数据而不是整个节点,而且寻找下一个节点并不能通过指针简单的++,所以需要把迭代器单独封装成一个类,通过重载*等运算符来完成需求。

namespace MyList
{//节点设计成结构体,方便访问template<typename T>struct list_node{list_node(const T val = T()):_data(val), _next(nullptr), _prev(nullptr){}T _data;list_node<T>* _next;list_node<T>* _prev;};//迭代器//这里设计模板参数除了迭代器,还有Ref(引用)和Ptr(指针)//这样设计是为了同时生成普通迭代器和const对象的迭代器//普通对象(可读可写):iterator<T, T&, T*>//const对象(可读不可写):const_iterator<T, const T&, const T*>template<typename T, typename Ref, typename Ptr>struct __list_iterator{typedef list_node<T> Node;typedef __list_iterator<T, Ref, Ptr> self; //要返回迭代器需要返回实例化对象,重命名一下Node* _node;__list_iterator(Node* p):_node(p){}self& operator++(){_node = _node->_next;return *this;}//后置++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;}Ref operator*(){return _node->_data;}//返回指针可以让自定义类型自行打印,访问成员//->操作符,比较特殊,it->_num转换出来其实是it.operator->()->_numPtr operator->(){return &(_node->_data);}bool operator!=(const self& s){return _node != s._node;}bool operator==(const self& s){return _node == s._node;}};//反向迭代器//反向迭代器需要进行封装,其实就是复用普通迭代器,然后++和--操作反过来//普通对象(可读可写):Reverse_iterator<iterator,T&,T*>//const对象(可读不可写):Reverse_iterator<const_iterator,const T&,const T*>template<class Iterator, class Ref, class Ptr>struct Reverse_iterator{typedef Reverse_iterator<Iterator, Ref, Ptr> self;Iterator _it;//构造Reverse_iterator(Iterator it):_it(it){}self& operator++(){_it--;return *this;}self operator++(int){self tmp(*this);_it--;return tmp;}self& operator--(){_it++;return *this;}self operator--(int){self tmp(*this);_it++;return tmp;}Ref operator*(){return *_it;}Ptr operator->(){return _it;}bool operator!=(const self& s){return _it != s._it;}bool operator==(const self& s){return _it == s._it;}};
}

2.完整代码

#pragma once
#include<iostream>
using namespace std;namespace MyList
{//节点设计成结构体,方便访问template<typename T>struct list_node{list_node(const T val = T()):_data(val), _next(nullptr), _prev(nullptr){}T _data;list_node<T>* _next;list_node<T>* _prev;};//迭代器//这里设计模板参数除了迭代器,还有Ref(引用)和Ptr(指针)//这样设计是为了同时生成普通迭代器和const对象的迭代器//普通对象(可读可写):iterator<T, T&, T*>//const对象(可读不可写):const_iterator<T, const T&, const T*>template<typename T, typename Ref, typename Ptr>struct __list_iterator{typedef list_node<T> Node;typedef __list_iterator<T, Ref, Ptr> self; //要返回迭代器需要返回实例化对象,重命名一下Node* _node;__list_iterator(Node* p):_node(p){}self& operator++(){_node = _node->_next;return *this;}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;}Ref operator*(){return _node->_data;}//返回指针可以让自定义类型自行打印,访问成员//->操作符,比较特殊,it->_num转换出来其实是it.operator->()->_numPtr operator->(){return &(_node->_data);}bool operator!=(const self& s){return _node != s._node;}bool operator==(const self& s){return _node == s._node;}};//反向迭代器//反向迭代器需要进行封装,其实就是复用普通迭代器,然后++和--操作反过来//普通对象(可读可写):Reverse_iterator<iterator,T&,T*>//const对象(可读不可写):Reverse_iterator<const_iterator,const T&,const T*>template<class Iterator, class Ref, class Ptr>struct Reverse_iterator{typedef Reverse_iterator<Iterator, Ref, Ptr> self;Iterator _it;Reverse_iterator(Iterator it):_it(it){}self& operator++(){_it--;return *this;}self operator++(int){self tmp(*this);_it--;return tmp;}self& operator--(){_it++;return *this;}self operator--(int){self tmp(*this);_it++;return tmp;}Ref operator*(){return *_it;}Ptr operator->(){return _it;}bool operator!=(const self& s){return _it != s._it;}bool operator==(const self& s){return _it == s._it;}};template<typename 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;typedef Reverse_iterator<iterator, T&, T*> reverse_iterator;typedef Reverse_iterator< const_iterator, const T&, const T*> reverse_const_iterator;//迭代器部分iterator begin(){return _head->_next;}iterator end(){return _head;}const_iterator begin()const{return _head->_next;}const_iterator end()const{return _head;}reverse_iterator rbegin(){return (--end());//_head->_prev}reverse_iterator rend(){return (end());//_head}reverse_const_iterator rbegin()const{return (--end());//_head->_prev}reverse_const_iterator rend()const{return (end());//_head}/// ///// private://不希望外界调用,设计成私有void empty_init(){_head = new Node;_head->_next = _head;_head->_prev = _head;_size = 0;}public://构造、析构部分list(){empty_init();}list(size_t n, const T& value = T()){empty_init();while (n--){push_back(value);}}//重载给内置类型使用,整形默认是int,不写这个会优先匹配list(Iterator first, Iterator last)list(int n, const T& value = T()){empty_init();while (n--){push_back(value);}}//迭代器区间初始化template <class Iterator>list(Iterator first, Iterator last){empty_init();while(first != last){push_back(*first);first++;}}list(const list<T>& lt){empty_init();for (auto e : lt){push_back(e);}}~list(){clear();delete _head;_head = nullptr;}//其它void swap(list<T> lt){std::swap(_size, lt._size);std::swap(_head, lt._head);}//使用传之传参,直接拷贝一份交换操作的底层空间就好list<T>& operator=(list<T> lt){swap(lt);return *this;}void clear(){iterator it = begin();while (it != end()){it = erase(it);}}/// ////访问头,尾数据T& front(){return _head->_next->_data;}const T& front()const{return _head->_next->_data;}T& back(){return _head->_prev->_data;}const T& back()const{return _head->_prev->_data;}/// ///// //增加删除部分void push_back(const T& val){insert(end(), val);}void push_front(const T& val){insert(begin(), val);}iterator insert(iterator pos, const T& val){Node* newnode = new Node(val);Node* cur = pos._node;Node* prev = cur->_prev;cur->_prev = newnode;newnode->_next = cur;newnode->_prev = prev;prev->_next = newnode;++_size;return newnode;}void pop_back(){erase(--end());}void pop_front(){erase(begin());}iterator erase(iterator pos){Node* cur = pos._node;Node* prev = cur->_prev;Node* next = cur->_next;prev->_next = next;next->_prev = prev;delete cur;--_size;return next;}//获取有效数据量size_t size(){return _size;}private:Node* _head;  //这里存储卫兵节点,因为底层是双向循环链表,可以找到头和尾size_t _size; //只需要在insert和erase里面加减就可以};}
http://www.lryc.cn/news/135382.html

相关文章:

  • 深度学习基础知识-pytorch数据基本操作
  • Springboot使用QueryDsl实现融合数据查询
  • 解决方案 | 电子签打通消费电子行业数智化经营通路
  • JVM理论知识
  • idea - 报错 Mybatis提示Tag name expected的问题< 小于号 无法识别
  • 合宙Air724UG LuatOS-Air LVGL API--对象
  • Java将PDF文件转为Word文档
  • vite创建项目命令
  • 解决Pandas KeyError: “None of [Index([...])] are in the [columns]“问题
  • 前端加springboot实现Web Socket连接通讯以及测试流程(包括后端实现心跳检测)
  • node使用高版本的oracledb导致连接oracle的Error: NJS-138异常
  • RabbitMQ手动签收消息
  • Unity 3d角色展示脚本(旋转 平移 缩放)展示界面
  • Spring Boot 将 Word 转换为 PDF
  • 【PHP面试题82】system和exec是用来做什么的?有什么区别
  • 05-微信小程序常用组件-表单组件
  • Lucky player —— Java 项目(Spring Boot)
  • ios 声网agora 音视频直播场景下的集成总结
  • mysql 、sql server 临时表、表变量、
  • 15. Canvas制作汽车油耗仪表盘
  • 解决git上传远程仓库时的最大文件大小限制
  • Midjourney API 国内申请及对接方式
  • 第一章 文件的输入和输出
  • java面试基础 -- 深克隆 浅克隆
  • 网络安全在医疗行业中的重要性
  • elemenPlus ElMessage 字符串如何换行问题
  • Linux socket网络编程
  • 【广州华锐互动】牲畜养殖VR模拟实操系统为传统教育注入新的生命力
  • JavaScript基础(Dom操作)
  • .NET6.0 System.Drawing.Common 通用解决办法