【C++ 】list 类

1. 标准库中的list类

list 类 的介绍:

1. list是可以在常数范围内在任意位置进行插入和删除的序列式容器,并且该容器可以前后双向迭代

2. list与forward_list非常相似:最主要的不同在于forward_list是单链表

3. 与其他的序列式容器相比(array,vector,deque),list通常在任意位置进行插入、移除元素的执行效率更好,只是不支持任意位置的随机访问

a. list 的构造函数

  • list() (无参构造函数)
  • list (const list& x) (拷贝构造)
  • list (InputIterator first, InputIterator last)

( 用[first, last)区间中的元素构造list )

  • list (size_type n, const value_type& val = value_type())

( 构造的list中包含n个值为val的元素 )

注意:

list 的迭代器是双向迭代器(完成 ++ , --),可以支持传单向迭代器( 完成 ++ ) 和双向迭代器

b. list 增删查改

  • push_back (尾插)
  • push_front (头插)
  • pop_back (尾删)
  • pop_front (头删)
  • insert (在某一位置前增加新节点)

代码举例1

#include <iostream>
#include <list>
using namespace std;int main()
{list<int> lt;lt.push_back(0);lt.push_back(10);lt.push_back(20);lt.push_back(30);list<int> ::iterator it = lt.begin();++it;lt.insert(it,70);it = lt.begin();while(it != lt.end()){cout << *it << endl;++it;}
}

运行结果:

  • earse (删除某一位置的节点)

代码举例2

#include <iostream>
#include <list>
using namespace std;int main()
{list<int> lt;lt.push_back(0);lt.push_back(10);lt.push_back(20);lt.push_back(30);list<int> ::iterator it = lt.begin();++it;lt.erase(it);it = lt.begin();while(it != lt.end()){cout << *it << endl;++it;}
}

运行结果:

  • swap ( 交换两个list中的元素 )

代码举例3

#include <iostream>
#include <list>
using namespace std;int main()
{list<int> lt;lt.push_back(10);lt.push_back(20);lt.push_back(30);list<int> llt;llt.push_back(4);llt.push_back(5);llt.push_back(6);lt.swap(llt);auto it = lt.begin();while(it != lt.end()){cout << *it << " ";++it;}cout << endl;for (auto i : llt){cout << i << " ";}
}

运行结果:

  • clear (清除有效节点,即不包括哨兵位)

c. list 容量

d. list 获取元素

e. list 迭代器

  • begin + end ( 返回第一个元素的迭代器+ 返回最后一个元素下一个位置的迭代器 )

画图分析

  • rbegin + rend ( 返回第一个元素的reverse_iterator,即end位置,返回最后一个元素下一个位置的 reverse_iterator,即begin位置 )

注意:

反向迭代器的模拟实现和我们理解的有偏差,图上为了理解,我们可以认为rbegin是最后一个元素,rend是第一个元素的前一个位置

但是实际上,rbegin指向的位置就是end的位置,rend指向的位置就是rbegin的位置,但是在解引用时,会运算符重载 *,得到该位置的上一个位置 (详情看 list 模拟实现)

2. 迭代器失效 

list 迭代器类似一个指针,指向节点的地址 (具体详情看 list 的模拟实现)

所以在发生 erase 的时候容易造成迭代器失效(即野指针)

3. list 类的模拟实现

代码

namespace lhy
{template<class T>
struct ListNode
{
public:ListNode* prev;ListNode* next;T val;ListNode(const T& t = T()){prev = next = nullptr;val = t;}
};
template<class T,class Ref,class Ptr>
class list_iterator
{
public:typedef list_iterator<T,Ref,Ptr>  self;list_iterator(ListNode<T>* n):_node(n){}Ptr operator->(){return &_node->val;}Ref operator*(){return _node->val;}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;}bool operator!=(const list_iterator& t){return _node != t._node;}bool operator==(const list_iterator& t){return _node == t._node;}ListNode<T>* _node;
};template<class iterator,class Ref,class Ptr>class list_converse_iterator{private:iterator com;public:typedef list_converse_iterator  self;list_converse_iterator(iterator& it):com(it){}Ptr operator->(){return &(*com);}Ref operator*(){iterator tmp = com;--tmp;return *tmp;}self& operator++(){--com;return *this;}self operator++(int){self tmp = *this;--*this;return tmp;}self& operator--(){++com;return *this;}self operator--(int){self tmp = *this;++*this;return tmp;}bool operator!=(const self& t){return com != t.com;}bool operator==(const self& t){return com == t.com;}};template<class T>class List{public:typedef list_iterator<T, T&, T*> iterator;typedef list_iterator<T, const T&, const T*>  const_iterator;typedef list_converse_iterator<iterator, T&, T*>  converse_iterator;typedef list_converse_iterator<iterator, const T&,const T*>  const_converse_iterator;List(){node = new ListNode<T>;node->next = node;node->prev = node;}iterator begin(){return iterator(node->next);}const const_iterator begin() const{return const_iterator(node->next);}iterator end(){return iterator(node);}const const_iterator end() const{return const_iterator(node);}converse_iterator rbegin(){return converse_iterator(end());}const_converse_iterator rbegin() const{return const_converse_iterator(end());}converse_iterator rend(){return converse_iterator(begin());}const_converse_iterator rend(){return const_converse_iterator(begin());}void push_back(const T& val){ListNode<T>* ptail = node->prev;ListNode<T>* newnode = new ListNode<T>(val);ptail->next = newnode;newnode->next = node;	newnode->prev = ptail;node->prev = newnode;}void push_front(const T& x){insert(begin(), x);}void insert(iterator pos,const T &x){ListNode<T>* cur = pos._node;ListNode<T>* newnode = new ListNode<T>(x);newnode->next = cur;newnode->prev = cur->prev;cur->prev->next = newnode;cur->prev = newnode;}void earse(iterator pos){ListNode<T>* cur = pos._node;assert(cur != node);ListNode<T>* _prev = cur->prev;ListNode<T>* _next = cur->next;_prev->next = _next;_next->prev = _prev;delete cur;}
private:ListNode<T>* node;
};
}

list 迭代器的实现 

单看这一个类的实现,可能会疑惑,已经有一个 List 类了,为什么还要加一个 list_iterator 类,并且很容易发现,两个类的成员变量是一样的

如:list<int> :: iterator it;

我们希望 *it 得到的是T类型的变量(这里是int 类型)

而 it++ 得到的是下一个节点的地址

如果是只有 List 类,无法实现

因为如果 typedef ListNode* iterator

那么 *it 的类型就是 ListNode;

it++ 也不是下一个结点的地址(这是链表,开辟的空间不是连续的)

所以这里的 list_iterator 类是为了运算符重载 *和++

代码注意事项

可以和下面的对应


 

注意:

const iterator 修饰的是 (ListNode<T>*),即指针不可以更改,但是指针所指向的内容可以更改

const_iterator 修饰的是 const ListNode<T>* ,即指针所指向的内容不可更改

注意:

对于这个运算符重载,实际写的时候只要写一个 -> 就行,编译器简化两个 ->

模板第一个传的是 正向迭代器,利用正向迭代器来实现反向迭代器的功能

这里 运算符重载* 让正向迭代器--再解引用,是为了得到 原先T 类型的数据的前一个数据,原因如下:

这里传递是 end() ,但是 对应的数据不是我们想要的

才会在解引用得到前一个数据的值

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

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

相关文章

白话微机:9.解释SoC和Linux

一. 前言&#xff08;回顾世界观&#xff09; 在“微机世界”&#xff0c;普通的城市(单片机)里&#xff0c;人又有一个别的名字叫做“数据”&#xff0c;人有0有1&#xff1b;人们也有住房&#xff0c;这些住房在这个世界叫做“存储器”&#xff1b;地上有路&#xff0c;这些路…

企业微信如何接入第三方应用?

1.登录企业微信管理后台&#xff1a;https://work.weixin.qq.com/wework_admin​​​​​ 2.点击创建应用&#xff1b; ​​​​​​​ 3. 此时可以看到已经创建好的应用&#xff0c;并且生成应用的唯一id&#xff08;agentId&#xff09; 4. 第三方应用申请域名 (举例&…

YOLOv9改进策略:注意力机制 |通道注意力和空间注意力CBAM | GAM超越CBAM,不计成本提高精度

&#x1f4a1;&#x1f4a1;&#x1f4a1;本文改进内容&#xff1a;通道注意力和空间注意力CBAM&#xff0c;全新注意力GAM&#xff1a;超越CBAM&#xff0c;不计成本提高精度 改进结构图如下&#xff1a; YOLOv9魔术师专栏 ☁️☁️☁️☁️☁️☁️☁️☁️☁️☁️☁️☁️…

【SystemVerilog】结构体真是太好用了~

前言 Verilog最大的缺陷之一是没有数据结构。在SystemVerilog中可以使用struct创建结构&#xff0c;struct只是把数据组织到一起&#xff0c;是数据的集合&#xff0c;所以是可综合的。 结构体是可以通过模块接口进行传递的&#xff0c;这就是本文想描述的内容。 一、结构体的…

VSSM VMamba实现

文章目录 VSSM维度变换初始化模型参数初始化模型搭建def_make_layerdef _make_downsample patch embed第一至四阶段分类器 VSSBlockdef __ init__ssm分支mlp分支 def forward VSSM Mamba实现可以参照之前的 mamba_minimal系列 论文地址&#xff1a; VMamba 论文阅读&#xff1…

c++入门你需要知道的知识点(上)

&#x1fa90;&#x1fa90;&#x1fa90;欢迎来到程序员餐厅&#x1f4ab;&#x1f4ab;&#x1f4ab; 今日主菜&#xff1a;c入门 主厨&#xff1a;邪王真眼 所属专栏&#xff1a;c专栏 主厨的主页&#xff1a;Chef‘s blog 前言&#xff1a; 咱也是好久没有更…

「THUWC 2017」大葱的神力 - 题解

忠告&#xff1a;如果你想抄题解的&#xff0c;可以离开&#xff0c;这不是一时半会儿能解决的问题 前置知识&#xff1a; 学习笔记&#xff1a;费用流https://blog.csdn.net/weixin_44043668/article/details/108738212C动态规划详解https://blog.csdn.net/weixin_51951103/a…

GPT实战系列-LangChain的OutPutParser解析器

GPT实战系列-LangChain的OutPutParser解析器 LangChain GPT实战系列-LangChain如何构建基通义千问的多工具链 GPT实战系列-构建多参数的自定义LangChain工具 GPT实战系列-通过Basetool构建自定义LangChain工具方法 GPT实战系列-一种构建LangChain自定义Tool工具的简单方法…

MySQL gh-ost DDL 变更工具

文章目录 1. MDL 锁介绍2. 变更工具3. gh-ost 原理解析4. 安装部署5. 操作演示5.1. 重点参数介绍5.2. 执行变更5.3. 动态控制 6. 风险提示 1. MDL 锁介绍 MySQL 的锁可以分为四类&#xff1a;MDL 锁、表锁、行锁、GAP 锁&#xff0c;其中除了 MDL 锁是在 Server 层加的之外&am…

strcmp的模拟实现

一&#xff1a;strcmp函数的定义&#xff1a; strcmp函数功能的解释&#xff1a; 比较两个字符串的大小&#xff08;按照字符串中字符的ascll码值&#xff09;。 标准规定&#xff1a; 第一个字符串大于第二个字符串&#xff0c;则返回大于 0 的数字 第一个字符串等于第二个…

宠物食品药品小程序有哪些功能

现在很多人都喜欢养宠物&#xff0c;这带动了宠物相关产业链&#xff0c;例如宠物医院、宠物清理、宠物食品、宠物玩具、宠物药品等。那么今天就介绍宠物食品药品小程序有哪些功能&#xff0c;以帮助您更好地为宠物行业的客户提供服务。 1. **商品展示**&#xff1a;宠物食品小…

C++ 作业 24/3/14

1、成员函数版本实现算术运算符的重载&#xff1b;全局函数版本实现算术运算符的重载 #include <iostream>using namespace std;class Test {friend const Test operator-(const Test &L,const Test &R); private:int c;int n; public:Test(){}Test(int c,int n…