STL中list的模拟实现

目录

list模拟实现

list节点

list的push_back()函数

list的迭代器操作(非const)

list的迭代器操作(const)

list迭代器const 非const优化

list的insert()函数

list的erase()函数

list的pop_back() push_front() pop_front()

list的clear()函数

list的empty()_Init函数与构造函数

list的拷贝构造

list的析构函数

list的赋值运算符重载

list的initializer_list

 项目文件

4.list与vector的对比


list模拟实现

list节点

首先看节点:list底层是一个带头双向循环链表

template <class T>
class list_Node{
public:T _data;list_Node<T>* _prev;list_Node<T>* _next;list_Node(const T& data):_data(data),_prev(nullptr),_next(nullptr){}
};
template <class T>
class list{
public:typedef list_Node<T> Node;list(){_head = new Node(T());//不能使用0初始化,因为类型不确定_head->_next = _head;_head->_prev = _head;}private:Node* _head;
}; 
}

list的push_back()函数

void push_back(const T& x){Node* newnode = new Node(x);Node* tail = _head->_prev;tail->_next = newnode;newnode->_prev = tail;newnode->_next = _head;_head->_prev = newnode;//insert(end(), x);
}

list的迭代器操作(非const)

list在物理上空间不是连续的,因此不可以像vector一样使用

typedef Node* iterator

Node* 不符合遍历的行为

List_iterator封装Node*

再通过重载运算符控制它的行为

因此这里我们可以使用一个类来封装使用

template <class T>
class List_iterator{
public:typedef list_Node<T> Node;typedef List_iterator Self;//不需要写析构函数,这个节点又不是这个类的//一个类一般不写析构,也就不需要显示写深拷贝List_iterator(Node* node):_node(node){}//日期类返回一个日期,那么迭代器返回一个迭代器//++itSelf& operator++(){_node = _node->_next;return *this;}//--itSelf& operator--(){_node = _node->_prev;return *this;}//it++Self operator++(int)//加参数 区分前置后置{Self tmp(*this);_node = _node->_next;return tmp;}//it--Self operator--(int){Self tmp(*this);_node = _node->_prev;return tmp;}T& operator*(){return _node->_data;}bool operator!=(const Self& it){return _node != it._node;}bool operator==(const Self& it){return _node == it._node;}T* operator->(){return &(_node->_data);}
private:Node* _node;};

注意: T* operator->()的意义,比如下面的例子

    struct Pos{int _x;int _y;Pos(int x = 0,int y = 0):_x(x),_y(y){}};nanyi::list<Pos> ls2;ls2.push_back(Pos(100,200));ls2.push_back(Pos(300,400));ls2.push_back(Pos(500,600));nanyi::list<Pos>::iterator it1 = ls2.begin();while (it1 != ls2.end()) {//cout << (*it1)._x << ":" << (*it1)._y << endl;// 为了可读性,省略了一个->cout << it1->_x << ":" << it1->_y << endl;//cout << it1.operator->()->_row << ":" << it1.operator->()->_col << endl;++it1;}cout << endl;return 0;

list类中

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

list的迭代器操作(const)

const迭代器不能在普通迭代器前加const修饰,比如这种情况

const迭代器的目标是 迭代器本身可以修改,指定的内容不能修改,类似const T* p

一旦加了不可以使用++it

因此我们重新写一个const类进行封装,我们只需改变解引用即可,使得指向内容不能修改

template <class T>
class const_List_iterator{
public:typedef list_Node<T> Node;typedef const_List_iterator Self;//不需要写析构函数,这个节点又不是这个类的//一个类一般不写析构,也就不需要显示写深拷贝const_List_iterator(Node* node):_node(node){}//日期类返回一个日期,那么迭代器返回一个迭代器//++itSelf& operator++(){_node = _node->_next;return *this;}//--itSelf& operator--(){_node = _node->_prev;return *this;}//it++Self operator++(int)//加参数 区分前置后置{Self tmp(*this);_node = _node->_next;return tmp;}//it--Self operator--(int){Self tmp(*this);_node = _node->_prev;return tmp;}const T& operator*(){return _node->_data;}bool operator!=(const Self& it){return _node != it._node;}bool operator==(const Self& it){return _node == it._node;}const T* operator->(){return &(_node->_data);}
private:Node* _node;}; 

list类中

   const_iterator begin()const{return const_iterator(_head->_next);}const_iterator end() const{return const_iterator(_head);}

代码是没有问题的,但是我们也可以发现,这样非常的冗余 有些函数是没有变化的,确多写了一遍,有没有什么办法能解决这种冗余呢?

他们的区别主要是返回参数不同,返回是否有const对象

因此我们可以增加模版参数,让编译器完成任务

list迭代器const 非const优化

template <class T,class Ref,class Ptr>
class List_iterator{
public:typedef list_Node<T> Node;typedef List_iterator Self;//不需要写析构函数,这个节点又不是这个类的//一个类一般不写析构,也就不需要显示写深拷贝List_iterator(Node* node):_node(node){}//日期类返回一个日期,那么迭代器返回一个迭代器//++itSelf& operator++(){_node = _node->_next;return *this;}//--itSelf& operator--(){_node = _node->_prev;return *this;}//it++Self operator++(int)//加参数 区分前置后置{Self tmp(*this);_node = _node->_next;return tmp;}//it--Self operator--(int){Self tmp(*this);_node = _node->_prev;return tmp;}Ref operator*(){return _node->_data;}bool operator!=(const Self& it){return _node != it._node;}bool operator==(const Self& it){return _node == it._node;}Ptr operator->(){return &(_node->_data);}
private:Node* _node;};

list的insert()函数

    iterator insert(iterator pos , const T& x){Node* cur = pos._node;Node* newnode = new Node(x);Node* prev = cur->_prev;newnode->_next = cur;newnode->_prev = prev;prev->_next = newnode;cur->_prev  = newnode;return iterator(newnode);}

由于不牵涉扩容问题 这里不存在迭代器失效

 

list的erase()函数

    iterator erase(iterator pos){assert(pos);Node* cur = pos._node;Node* prev = cur->_prev;Node* next = cur->_next;// prev cur nextprev->_next = next;next->_prev = prev;delete cur;cur = nullptr;return iterator(next);}

这里存在迭代器失效,因此我们和库里一样返回下一个节点迭代器

list的pop_back() push_front() pop_front()

    void pop_back(){erase(--end());}void push_front(const T& x){insert(begin(), x);}void pop_front(){erase(begin());}

list的clear()函数

    void clear(){auto it = begin();while (it != end()) {it = erase(it);//不能++it,首先会迭代器失效,其次erase会自动返回下一个位置}}

list的empty()_Init函数与构造函数

由于我们经常使用申请头节点,因此我们把头节点封装成一个函数,便于调用

    void empty_Init(){_head = new Node(T());//不能使用0初始化,因为类型不确定_head->_next = _head;_head->_prev = _head;}list(){empty_Init();}

list的拷贝构造

如果我们不显示写拷贝构造 ,那么编译器会默认生成一个浅拷贝

浅拷贝生成的ls2,与ls1的地址相同,对ls1操作也会对ls2有影响

因此我们需要显示的写拷贝构造

    //拷贝构造list(const list<T>& ls){empty_Init();for (const auto& e : ls) {//范围for不确定类型,加引用push_back(e);}}

 

list的析构函数

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

list的赋值运算符重载

    //ls2 = ls1list<T>& operator=(list<T> ls){std::swap(_head, ls._head);return *this;}

list的initializer_list

可以将花括号内容,直接赋值给对象

    list (initializer_list<T> il){empty_Init();for (const auto& e : il) {push_back(e);}}

 项目文件

//
//  list.hpp
//  List
//
//  Created by 南毅 on 2024/5/28.
//#include <iostream>
using namespace std;namespace nanyi {
template <class T>
class list_Node{
public:T _data;list_Node<T>* _prev;list_Node<T>* _next;list_Node(const T& data):_data(data),_prev(nullptr),_next(nullptr){}
};template <class T,class Ref,class Ptr>
class List_iterator{
public:typedef list_Node<T> Node;typedef List_iterator Self;//不需要写析构函数,这个节点又不是这个类的//一个类一般不写析构,也就不需要显示写深拷贝List_iterator(Node* node):_node(node){}//日期类返回一个日期,那么迭代器返回一个迭代器//++itSelf& operator++(){_node = _node->_next;return *this;}//--itSelf& operator--(){_node = _node->_prev;return *this;}//it++Self operator++(int)//加参数 区分前置后置{Self tmp(*this);_node = _node->_next;return tmp;}//it--Self operator--(int){Self tmp(*this);_node = _node->_prev;return tmp;}Ref operator*(){return _node->_data;}bool operator!=(const Self& it){return _node != it._node;}bool operator==(const Self& it){return _node == it._node;}Ptr operator->(){return &(_node->_data);}
public:Node* _node;};//template <class T>
//class const_List_iterator{
//public:
//    typedef list_Node<T> Node;
//    typedef const_List_iterator Self;
//    
//    //不需要写析构函数,这个节点又不是这个类的
//    //一个类一般不写析构,也就不需要显示写深拷贝
//    const_List_iterator(Node* node)
//    :_node(node)
//    {
//        
//    }
//    
//    //日期类返回一个日期,那么迭代器返回一个迭代器
//    //++it
//    Self& operator++(){
//        _node = _node->_next;
//        return *this;
//    }
//    //--it
//    Self& operator--(){
//        _node = _node->_prev;
//        return *this;
//    }
//    //it++
//    Self operator++(int)//加参数 区分前置后置
//    {
//        Self tmp(*this);
//        _node = _node->_next;
//        return tmp;
//    }
//    //it--
//    Self operator--(int){
//        Self tmp(*this);
//        _node = _node->_prev;
//        return tmp;
//    }
//    
//    
//    const T& operator*(){
//        return _node->_data;
//    }
//    
//    bool operator!=(const Self& it){
//        return _node != it._node;
//    }
//    
//    bool operator==(const Self& it){
//        return _node == it._node;
//    }
//    
//    const T* operator->(){
//        return &(_node->_data);
//    }
//private:
//    Node* _node;
//    
//};template <class T>
class list{
public:typedef list_Node<T> Node;typedef List_iterator<T,T&,T*> iterator;typedef List_iterator<T,const T&,const T*> const_iterator;//typedef const_List_iterator<T> const_iterator;void empty_Init(){_head = new Node(T());//不能使用0初始化,因为类型不确定_head->_next = _head;_head->_prev = _head;}list(){empty_Init();}void clear(){auto it = begin();while (it != end()) {it = erase(it);//不能++it,首先会迭代器失效,其次erase会自动返回下一个位置}}//拷贝构造list(const list<T>& ls){empty_Init();for (const auto& e : ls) {push_back(e);}}//析构~list(){clear();delete _head;_head = nullptr;}void push_back(const T& x){
//        Node* newnode = new Node(x);
//        Node* tail = _head->_prev;
//        
//        tail->_next = newnode;
//        newnode->_prev = tail;
//        newnode->_next = _head;
//        _head->_prev = newnode;insert(end(), x);}void pop_back(){erase(--end());}void push_front(const T& x){insert(begin(), x);}void pop_front(){erase(begin());}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);}iterator insert(iterator pos , const T& x){Node* cur = pos._node;Node* newnode = new Node(x);Node* prev = cur->_prev;newnode->_next = cur;newnode->_prev = prev;prev->_next = newnode;cur->_prev  = newnode;return iterator(newnode);}iterator erase(iterator pos){Node* cur = pos._node;Node* prev = cur->_prev;Node* next = cur->_next;// prev cur nextprev->_next = next;next->_prev = prev;delete cur;cur = nullptr;return iterator(next);}//ls2 = ls1list<T>& operator=(list<T> ls){std::swap(_head, ls._head);return *this;}list (initializer_list<T> il){empty_Init();for (const auto& e : il) {push_back(e);}}
public:Node* _head;
};
}

4.list与vector的对比

vector与list都是STL中非常重要的序列式容器,由于两个容器的底层结构不同,导致其特性以及应用场景不同,其主要不同如下:

 

 

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

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

相关文章

M3/M4 Ultra Mac Pro:你需要知道的一切

本文翻译自&#xff1a;M3/M4 Ultra Mac Pro: Everything you need to know (By Roman Loyola2024年5月15日 ) https://www.macworld.com/article/2320613/m3-m4-ultra-mac-pro-everything-you-need-to-know.html 文章目录 Mac Pro M3/M4&#xff1a;发布日期Mac Pro M3/M4&…

代码随想录|Day34|贪心算法 part03|● 1005.K次取反后最大化的数组和 ● 134. 加油站● 135. 分发糖果

1005.K次取反后最大化的数组和 class Solution: def largestSumAfterKNegations(self, nums: List[int], k: int) -> int: nums.sort() for i in range(len(nums)): if nums[i] < 0 and k > 0: nums[i] -nums[i] k - 1 if k % 2 1: nums.sort() nums[0] *…

C语言网页编程:深度探索与实践挑战

C语言网页编程&#xff1a;深度探索与实践挑战 在编程的广阔领域中&#xff0c;C语言以其高效、稳定和接近底层的特性著称。然而&#xff0c;当提及C语言与网页编程的结合时&#xff0c;许多人可能会感到困惑。毕竟&#xff0c;C语言通常被视为系统级编程的利器&#xff0c;而…

Expression #1 of SELECT list is not in GROUP BY clause and

mybatis-plus mysql8.0 group 1055 - Expression #1 报错信息 "mybatis-plus mysql8.0 group 1055 - Expression #1" 指的是在使用MyBatis-Plus操作MySQL数据库时遇到了错误。这个错误通常是由于MySQL 8.0版本的SQL模式中引入了ONLY_FULL_GROUP_BY&#xff0c;这个规…

JAVA:使用ServerSocket实现多线程的服务器功能

目录 一、服务端 二、Task任务类。 三、客户端请求 一、服务端 创建数据源&#xff0c;并实现ServerSocket&#xff0c;当有连接进来时&#xff0c;创建Task任务类&#xff0c;交由线程池处理&#xff0c;主程序在s.accept();继续等待新的连接&#xff08;这个等待是阻塞制的…

手写一个vue2的diff案例

一、Vue为什么需要采用虚拟DOM&#xff1f; 虚拟 DOM 在 Vue 中起到了优化性能、提供跨平台兼容性 以及简化开发流程的作⽤。 虚拟 DOM 可以减少直接操作实际 DOM 的次数。虚拟 DOM 是⼀个抽象层&#xff0c;将实际 DOM 抽象为⼀个跨平台 的表示形式。使得vue 可以在不同的平…

yangwebrtc x86_64环境搭建

版本&#xff1a;5.0.099 sudo apt-get install libxext-dev sudo apt-get install x11proto-xext-dev sudo apt-get install libxi-dev sudo apt install libasound2-dev sudo apt install libgl1-mesa-dev sudo apt-get install libxtst-dev 用qt打开以下两个项目的.pro met…

WPF快速学习入门(8.视觉树和逻辑树)

WPF视觉树和逻辑树学习教程 WPF&#xff08;Windows Presentation Foundation&#xff09;是一种用于构建桌面应用程序的UI框架。在WPF中&#xff0c;有两种重要的树结构&#xff1a;视觉树和逻辑树。本文将介绍这两种树结构的概念、用途以及如何在实际项目中使用它们。 1. 视…

探索UWB模块的多功能应用——UWB技术赋能智慧生活

超宽带&#xff08;Ultra-Wideband, UWB&#xff09;技术&#xff0c;凭借其高精度、低功耗和强抗干扰能力&#xff0c;正在成为智能家居领域的一项关键技术。UWB模块的应用不仅提高了智能家居设备的性能&#xff0c;还为家庭安全、设备管理和用户体验带来了显著的改善。 UWB模…

pycharm 上一次编辑位置不见了

目录 pycharm2024版 上一次编辑位置不见了&#xff0c;研究发现移到了左下角了&#xff0c;如下图所示&#xff1a; 上一次编辑位置快捷键&#xff1a; pycharm2024版 上一次编辑位置不见了&#xff0c;研究发现移到了左下角了&#xff0c;如下图所示&#xff1a; 上一次编辑…

Windows 80端口占用解决办法

启动软件系统前&#xff0c;有时遇见端口被其他程序占用&#xff0c;导致无法启动系统 解决办法: # 查看端口占用情况 > netstat -ano | findstr 端口号通常端口占用&#xff0c;通过任务管理器查看PID&#xff0c;结束任务可以完成。System进程占用&#xff0c;结束不了&…

惯性动作捕捉与数字人实时交互/运营套装,对高校元宇宙实训室有何作用?

惯性动作捕捉与数字人实时交互/运营套装&#xff0c;可以打破时空限制&#xff0c;通过动捕设备写实数字人软件系统动捕设备系统定制化数字人短视频渲染平台&#xff0c;重塑课程教学方式&#xff0c;开展元宇宙沉浸式体验教学活动和参观交流活动。 写实数字人软件系统内置丰富…

AI预测福彩3D采取888=3策略+和值012路一缩定乾坤测试5月31日预测第7弹

昨天的3D已命中&#xff01;今天继续基于8883的大底&#xff0c;使用尽可能少的条件进行缩号。好了&#xff0c;直接上结果吧~ 首先&#xff0c;888定位如下&#xff1a; 百位&#xff1a;7,6,5,8,9,3,2,0 十位&#xff1a;3,4,5,2,1,7,8,9 …

视频汇聚EasyCVR平台GA/T 1400视图库应用:助力社会治安防控效能提升

在信息化、智能化的时代浪潮下&#xff0c;公安视频图像信息应用系统的发展与应用显得尤为重要。GA/T 1400标准&#xff0c;全称为《公安视频图像信息应用系统》&#xff0c;作为公安行业的一项重要标准&#xff0c;其视图库的应用在提升公安工作效能、加强社会治安防控等方面发…

C#中的空合并运算符与空合并赋值运算符:简化空值处理

在C#编程中&#xff0c;处理可能为null的值是一项常见的任务&#xff0c;尤其是在涉及数据库查询、Web服务调用或任何可能返回缺失数据的场景中。为了简化这类操作并提高代码的可读性&#xff0c;C# 8 引入了两个非常实用的运算符&#xff1a;空合并运算符 (??) 和 空合并赋值…

小白跟做江科大32单片机之按键控制LED

原理部分 1.LED部分使用的是这样的连接方式 2.传感器模块的电路图 滤波电容如果接地&#xff0c;一般用于滤波&#xff0c;在分析电路时就不用考虑。下面这个电路就是看A端和B端哪端的拉力大&#xff0c;就能把电压值对应到相应的电压值 比较器部分 如果A端电压>B端电压&am…

深入Kafka消息分区机制:从原理到实践

深入Kafka消息分区机制&#xff1a;从原理到实践 在现代分布式系统中&#xff0c;如何高效地处理海量数据是一个至关重要的问题。Apache Kafka作为一种高吞吐量的分布式消息系统&#xff0c;广泛应用于日志收集、实时分析等场景。为了保证数据的高效处理和系统的高可扩展性&am…

【C++奇技淫巧】CRTP(奇特重现模板模式)

CRTP&#xff08;Curiously Recurring Template Pattern&#xff0c;奇特重现模版模式&#xff09;,是一种在C中使用模板来实现的设计模式&#xff0c;主要用于实现编译时多态性&#xff08;静态多态&#xff09;。这种模式通过类模板和模板继承机制来实现&#xff0c;使得派生…

【html知识】html中常用的表单元素+css格式美化

创作背景与目的&#xff1a; 随着互联网的飞速发展&#xff0c;表单作为网页交互的重要组成部分&#xff0c;扮演着收集用户信息、进行用户反馈的关键角色。本作品旨在总结并展示HTML中常用的表单元素&#xff0c;帮助开发者快速了解并应用这些元素&#xff0c;以优化网页的交…

自学成才Flutter 弹性布局、线性布局

本文我们要介绍 Flutter 中布局 Widget&#xff0c;包括弹性布局、线性布局 流式布局和层叠布局。 Flutter中文网 Flutter开发 一、弹性布局--Flex Flex 类似 Android 中的 FlexboxLayout&#xff0c;和 Expanded 配合使用可以实现子Widget 按照一定比例来分配父容器空间。 使…