C++ : list类及其模拟实现

ops/2024/9/23 15:28:35/

目录

一、list的介绍和使用

list的介绍

list的使用

1.list的构造

构造函数

2.list iterator 的使用

3.list capacity

4.list element access

5.list modifiers

6.list的迭代器失效

二、list的模拟实现

要点

list类模拟实现部分接口全部代码展示


一、list的介绍和使用

list的介绍

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

2.list的底层是带头双向可循环链表结构,双向链表中每个元素存储在互不相关的独立节点中,在节点中通过指针指向其前一个元素和后一个元素。

3.list和forward_list非常相似:最主要的不同在于forward_list 是单链表,只能朝前迭代,让其更简单高效。

4.与其他的序列式容器相比(array,vector,deque),list通常在任意位置进行插入、移除元素的执行效率更好。

5.与其他序列式容器相比,list和forward_list最大的缺陷是不支持任意位置的随机访问,比如:要访问list的第6个元素,必须从已知的位置(比如头部或者尾部)迭代到该位置,在这段位置上迭代需要线性的时间开销;list还需要一些额外的空间。

list的使用

1.list的构造

构造函数

(1)是指传来一个空指针,构造空的list

(2)指构造n个值为val的list

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

(4)用x链表拷贝构造

2.list iterator 的使用

迭代器可以理解成一个指针,因为它封装了底层的逻辑,像指针一样使用。该指针指向list中的某个节点。begin就是指向第一个节点(非哨兵),end,返回最后一个元素下一个位置(就是哨兵位)。rbegin返回end位置,rend,返回begin位置。

注意:

begin和end是正向迭代器,对迭代器++,迭代器向后移动

rbegin和rend是反向迭代器,对迭代器++,迭代器向前移动

3.list capacity

empty 检查list是否是空链表

size  返回list中有效节点个数

4.list element access

front 返回list中,第一个节点的值的引用

back返回list中,最后一个节点的值的引用

5.list modifiers

模拟实现的话,掌握红框中常用的就好。

push_front 在list首节点前插入 值为val的节点

pop_front 删除list首节点

push_back 在list最后一个元素后插入值为val的节点

pop_back 删除list尾结点

insert 在pos位置插入一个值为val的节点

erase 删除pos位置的节点

swap  交换两个list中的元素

clear 清除list中的有效节点

6.list的迭代器失效

迭代器失效是指迭代器指向的节点无效,也就是该节点被删除了,用户不会再有该节点的访问权限。因为list的底层是带头双向循环链表,所以在list中进行插入时是不会导致list迭代器失效的,只有在删除时才会失效,并且失效的只有被删除节点的迭代器,其他节点的迭代器不会受到影响。

在逐个(调用erase函数时,参数)用后置++,不用前置++。表示删除以后,it又被重新赋值了。

二、list的模拟实现

要点

1.list要实现节点链表还有迭代器三个部分,分别建立三个类模板。这是为了提高类的复用性。封装在自己的命名空间里。

2.单个节点的结构体是公有的开放的,因为要在其他类里经常调用。所以得开放。包含val,前指针和后指针,以及构造函数。

 // List的节点类template<class T>struct ListNode{ListNode(const T& val = T()):_pPre(nullptr), _pNext(nullptr), _val(val){}ListNode<T>* _pPre;ListNode<T>* _pNext;T _val;};

3.写迭代器的类时,迭代器指针也要设置为开放的,为了后面在list类中erase  pos位置和insert pos位置可以访问。迭代器的类模板是这样的。

Ref 和Ptr分别是迭代器返回的引用类型  和 迭代器返回的指针类型。

如果写T& 和T*,会限制迭代器返回的类型,这样想要返回const T& 和const T* 也无法更改。而Ref可以使你在实例化的时候就可以选择返回类型是什么。

//List的迭代器类
template<class T, class Ref, class Ptr>
class ListIterator
{typedef ListNode<T>* PNode;typedef ListIterator<T, Ref, Ptr> Self;//加了Ref和Ptr后,代码的灵活性提高
public:                                 //体现在返回的时候和实例化的时候,我可以控制迭代器解引 //用后返回什么类型ListIterator(PNode pNode = nullptr):_pNode(pNode){}ListIterator(const Self& l):_pNode(l._pNode){}Ref operator*(){return _pNode->_val;}Ptr operator->(){return &_pNode->_val;}Self& operator++(){_pNode = _pNode->_pNext;return *this;}Self operator++(int){Self ptmp(*this);_pNode = _pNode->_pNext;return ptmp;}Self& operator--(){_pNode = _pNode->_pPre;return *this;}Self& operator--(int){Self ptmp(*this);_pNode = _pNode->_pPre;return ptmp;}bool operator!=(const Self& l){return _pNode != l._pNode;}bool operator==(const Self& l){return _pNode == l._pNode;}
public:PNode _pNode;
};

list类模拟实现部分接口全部代码展示

#pragma once
#include<iostream>
#include <assert.h>
using namespace std;namespace ting
{// List的节点类template<class T>struct ListNode{ListNode(const T& val = T()):_pPre(nullptr), _pNext(nullptr), _val(val){}ListNode<T>* _pPre;ListNode<T>* _pNext;T _val;};//List的迭代器类template<class T, class Ref, class Ptr>class ListIterator{typedef ListNode<T>* PNode;typedef ListIterator<T, Ref, Ptr> Self;//加了Ref和Ptr后,代码的灵活性提高public:                                 //体现在返回的时候和实例化的时候,我可以控制迭代器解引用后返回什么类型ListIterator(PNode pNode = nullptr):_pNode(pNode){}ListIterator(const Self& l):_pNode(l._pNode){}Ref operator*(){return _pNode->_val;}Ptr operator->(){return &_pNode->_val;}Self& operator++(){_pNode = _pNode->_pNext;return *this;}Self operator++(int){Self ptmp(*this);_pNode = _pNode->_pNext;return ptmp;}Self& operator--(){_pNode = _pNode->_pPre;return *this;}Self& operator--(int){Self ptmp(*this);_pNode = _pNode->_pPre;return ptmp;}bool operator!=(const Self& l){return _pNode != l._pNode;}bool operator==(const Self& l){return _pNode == l._pNode;}public:PNode _pNode;};//list类template<class T>class list{typedef ListNode<T> Node;typedef Node* PNode;public:typedef ListIterator<T, T&, T*> iterator;typedef ListIterator<T, const T&, const T*> const_iterator;public:///// List的构造list(){//处理私设的变量CreateHead();}list(int n, const T& value = T()){CreateHead();while (n--){push_back(value);}}template <class Iterator>list(Iterator first, Iterator last){CreateHead();while (first != last){push_back(*first);++first;}}list(const list<T>& l){CreateHead();list<T> tmp(l.begin(), l.end());swap(tmp);}list<T>& operator=(const list<T> l){swap(l);return *this;}~list(){clear();delete _pHead;_pHead = nullptr;}///// List Iteratoriterator begin(){return iterator(_pHead->_pNext);//因为_pNext不是迭代器类型,要走迭代器的拷贝构造}iterator end(){return iterator(_pHead);}const_iterator begin() const{return const_iterator(_pHead->_pNext);}const_iterator end() const{return const_iterator(_pHead);}///// List Capacitysize_t size()const{size_t n = 0;for (auto it = begin(); it != end(); ++it){++n;}return n;}bool empty()const{return _pHead->_pNext == _pHead;}// List AccessT& front(){return _pHead->_pNext->_val;}const T& front()const{return _pHead->_pNext->_val;}T& back(){return _pHead->_pPre->_val;}const T& back()const{return _pHead->_pPre->val;}// List Modifyvoid push_back(const T& val) { insert(end(), val); }void pop_back() { erase(--end()); }void push_front(const T& val) { insert(begin(), val); }void pop_front() { erase(begin()); }// 在pos位置前插入值为val的节点iterator insert(iterator pos, const T& val){PNode newnode = new Node(val);newnode->_pNext = pos._pNode;newnode->_pPre = pos._pNode->_pPre;newnode->_pPre->_pNext = newnode;pos._pNode->_pPre = newnode;return iterator(newnode);}iterator erase(iterator pos){assert(pos != end());PNode nextNode = pos._pNode->_pNext;pos._pNode->_pPre->_pNext = pos._pNode->_pNext;pos._pNode->_pNext->_pPre = pos._pNode->_pPre;delete pos._pNode;return iterator(nextNode);}void clear(){while (!empty()){pop_back();}}void swap(list<T>& l){std::swap(_pHead, l._pHead);}private:void CreateHead(){_pHead = new Node();_pHead->_pPre = _pHead;_pHead->_pNext = _pHead;}PNode _pHead;};
};


http://www.ppmy.cn/ops/33619.html

相关文章

【DPU系列之】如何通过带外口登录到DPU上的ARM服务器?(Bluefield2举例)

文章目录 1. 背景说明2. 详细操作步骤2.1 目标拓扑结构2.2 连接DPU带外口网线&#xff0c;并获取IP地址2.3 ssh登录到DPU 3. 进一步看看系统的一些信息3.1 CPU信息&#xff1a;8核A723.2 内存信息 16GB3.3 查看ibdev设备 3.4 使用小工具pcie2netdev查看信息3.5 查看PCIe设备信息…

企业电子投票系统开发总结:技术细节与实践心得

全套资料、论文、源代码传送门 引言 在数字化时代&#xff0c;电子投票系统因其便捷性和高效性&#xff0c;成为企业决策过程中不可或缺的工具。本次介绍的课程设计开发了一个企业级电子投票系统&#xff0c;旨在为企业提供一个稳定、安全且用户友好的投票平台。以下是我在开…

滑动窗口详解

目录 一、滑动窗口的特定步骤&#xff1a; 二、题目解析 1、⻓度最⼩的⼦数组---点击跳转题目 3、最⼤连续 1 的个数 III----点击跳转题目 4、将 x 减到 0 的最⼩操作数----点击跳转题目 5、⽔果成篮----点击跳转题目 滑动窗口是双指针算法中细分的一种&#xff0c;它由暴…

rust使用Atomic创建全局变量和使用

Mutex用起来简单&#xff0c;但是无法并发读&#xff0c;RwLock可以并发读&#xff0c;但是使用场景较为受限且性能不够&#xff0c;那么有没有一种全能性选手呢&#xff1f; 欢迎我们的Atomic闪亮登场。 从 Rust1.34 版本后&#xff0c;就正式支持原子类型。原子指的是一系列…

RabbitMQ知识点总结(一)

为什么要使用RabbitMQ? 异步&#xff0c;解耦&#xff0c;削峰。 异步 提高效率&#xff1b;一个挂了&#xff0c;另外的服务不受影响。 解耦 增加或减少服务比较方便。 削峰 每天0点到16点&#xff0c;A系统风平浪静&#xff0c;每秒并发数量就100个。结果每次到了16点到…

C语言 void 指针就是空指针吗?它有什么作⽤?

一、问题 这是⼀个在⾯试时很容易出现的问题&#xff0c;但是也是很多⼈混淆的问题&#xff0c;这个问题如何回答&#xff1f; 二、解答 void 指针⼀般称为通⽤指针&#xff0c;要与空指针严格区分。void 指针⽤于指向⼀个不属于任 何类型的对象&#xff0c;所以 void 指针称为…

临床+康复的一体化治疗服务,把握黄金康复时间

随着我院脑血管病人&#xff0c;重症病人及骨科病人康复需求的日渐增多&#xff0c;为了使每位住院患者在治疗原发病的同时&#xff0c;第一时间接受到康复治疗&#xff0c;提高病人的生活质量&#xff0c;降低致残率&#xff0c;我院康复治疗科在院领导的大力支持下&#xff0…

【JDBC】Apache DbUtils工具类使用

1 简介 Commons DbUtils是Apache 组织提供的一个对]DBC进行简单封装的开源工具类库&#xff0c;简化JDBC应用程序的开发&#xff0c;同时也不会影响程序的性能&#xff0c;是一个小巧简单实用的工具。对于数据表的读操作&#xff0c;可以把结果转换成List、Array、Set等java集…