STL中list的模拟实现

news2024/12/29 2:41:50

目录

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)
    {
        
    }
    
    //日期类返回一个日期,那么迭代器返回一个迭代器
    //++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;
    }
    
    
    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)
    {
        
    }
    
    //日期类返回一个日期,那么迭代器返回一个迭代器
    //++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;
    
}; 

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)
    {
        
    }
    
    //日期类返回一个日期,那么迭代器返回一个迭代器
    //++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;
    }
    
    
    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 next
        
        prev->_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 = ls1
    list<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)
    {
        
    }
    
    //日期类返回一个日期,那么迭代器返回一个迭代器
    //++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;
    }
    
    
    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 next
        
        prev->_next = next;
        next->_prev = prev;
        delete cur;
        cur = nullptr;
        return iterator(next);
    }
    
    //ls2 = ls1
    list<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.coloradmin.cn/o/1718410.html

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

相关文章

【Python】解决Python报错:AttributeError: ‘int‘ object has no attribute ‘xxx‘

&#x1f9d1; 博主简介&#xff1a;阿里巴巴嵌入式技术专家&#xff0c;深耕嵌入式人工智能领域&#xff0c;具备多年的嵌入式硬件产品研发管理经验。 &#x1f4d2; 博客介绍&#xff1a;分享嵌入式开发领域的相关知识、经验、思考和感悟&#xff0c;欢迎关注。提供嵌入式方向…

使用element的提示框并修改css

使用el-tooltip来做提示框&#xff1a; <el-tooltip popper-class"popper" content"敬请期待" placement"right"><div><i class"iconfont icon-lianjie-01"></i><span>输入链接</span></div&…

从头开始构建GPT标记器

从头开始构建GPT标记器 对于GPT Tokenizer&#xff0c;论文《Language Models are Unsupervised Multitask Learners》中介绍了一种字节级编码作为LLM的标记化机制&#xff1a; The vocabulary is expanded to 50,257. We also increase the context size from 512 to 1024 to…

LAMP分布式安全方案搭建网页 (LinuxCentOS7+Apache+Mariadb+PHP)包括服务端口及防火墙规则配置

目录 一、实验目的 二、设计方案及规划 三、实验内容及步骤 &#xff08;1&#xff09;实验前基础配置 &#xff08;2&#xff09;Test配置&#xff0c;安装Firefox浏览器和图形界面 &#xff08;3&#xff09;Web安装Apache &#xff08;4&#xff09;Database安装Mari…

变现实谈,我要的不是灵光一现,而是真实的实现!——感悟篇

变现要的是行动不是想法 正文时代奇点奇迹 点题以己及人 正文 每当我看到了一个有趣的事情 我会在脑中构思一些想法 会贴合我当下的想要做的事情 比如 在我写下这篇文章之前 我看到了 二战期间的诞生的一个奇迹 可口可乐 我就思考 咦 原来可口可乐居然是在这么个时间点成长…

【React】函数式组件的 ref 结果为 undefined(useRef)

函数式组件不能直接应用 ref。 class ClassComp extends Component {render() {return ;} }function FuncComp(props) {return ; }function App() {let refClass useRef();let refFunc useRef();return (<div><ClassComp ref{refClass} /><FuncComp ref{refF…

计算机网络-BGP路由通告原则

前面一章我们学习了BGP的路由产生方式以及查看路由表信息。BGP自身并不会发现并计算产生路由。BGP通过network、import-route、aggregate聚合方式生成BGP路由后&#xff0c;通过Update报文将BGP路由传递给对等体。那BGP向邻居通告路由时遵循哪些原则呢&#xff1f; BGP通告遵循…

将局部变量指针传递给某个c++类,离开类时数据发生变化

最近遇到一个c的问题&#xff0c;将一个局部变量的值传递给某个类&#xff0c;类中没有对该数据进行任何显式修改&#xff0c;结果该变量的值发生变化并且不可访问。 我开始很奇怪为何会发生这样的事情&#xff0c;后来经过调试&#xff0c;发现原来是该类发生了异常&#xff…

【vue】v-for只显示前几个数据,超出显示查看更多

v-for只显示前几个数据&#xff0c;超出显示查看更多 如图 <div v-for"(item,index) in list.slice(0,3)" :key"index"><div class"degreeNo" :class"index0?noOne:index1?noTwo:index2?noThree:"> NO{{index1}}:<…

基于springboot的-仓库 管理系统(附:源码+课件)

项目介绍002&#xff1a; 管理员system 123456 客户表&#xff08;ID 客户名称 邮编 客户地址 客户电话 联系人 联系人电话 开户行账号 邮箱&#xff09; 供应商表&#xff08;ID 供应商名称 邮编 供应商地址 供应商电话 联系人 联系人电话 开户行 账号 邮箱&#xff09; 商品…

go 针对 time类型字段,前端查询,后端返回数据格式为UTC时间

测试代码 package mainimport ("context""log""net/http""time""github.com/gin-gonic/gin""go.mongodb.org/mongo-driver/bson""go.mongodb.org/mongo-driver/bson/primitive""go.mongodb.org/m…

控制障碍函数CBF详解(附带案例实现)

控制障碍函数CBF详解&#xff08;附带案例实现&#xff09; 文章目录 控制障碍函数CBF详解&#xff08;附带案例实现&#xff09;1. Control Affine System2. Lyapunov Theory, Nagumos Theory, Invariance Principle3. Control Lyapunov Function (CLF) and CLF-QP4. Control …

[openwrt-21.02]openwrt-21.02 make menuconfig不显示luci-app-firewall问题分析及解决方案

问题描述 make menuconfig在 在applications界面没有luci-app-firewall 问题分析 首先重新执行 ./scripts/feeds update -a ./scripts/feeds install -a 然后再次执行make menuconfig&#xff0c;依然不显示&#xff0c;所以不是feeds安装的问题 最后看到log有个openmptc…

字符串匹配算法(二)BM算法

文章目录 算法简介坏字符规则坏字符的定义坏字符的移动 好后缀规则好后缀的定义好后缀的移动 算法实现 算法简介 BM算法也就是Boyer Moore算法&#xff0c;它是一种非常高效的字符串匹配算法&#xff0c;是一种滑动算法。什么是滑动&#xff1f; 下面例子中&#xff0c;主串中…

vcruntime140_1.dll在哪个文件夹?详细修复vcruntime140_1.dll缺失的方法

vcruntime140_1.dll文件是什么&#xff1f;相信很多人都对它很陌生吧&#xff1f;毕竟大部分人对于dll文件还是了解得太少了&#xff0c;当突发情况出现vcruntime140_1.dll文件丢失&#xff1f;你要怎么办&#xff1f;不要担心&#xff0c;下面我们就来给大家详细的讲解一下修复…

出吉林大学计算机考研资料适用于计专966/计学941/软专967

本人是24上岸吉大计算机专硕的考生&#xff0c;先上成绩&#xff1a; 出专业课备考过程的相关笔记资料&#xff0c;也可以提供经验分享等&#xff1a; 吉林大学计算机数据结构基础算法ADL汇总&#xff0c;适用于计专966/计学941/软专967综合整理小绿书以及期末题上重难点算法…

LLVM入门教学——Code Coverage插桩

1、简介 LLVM的Code Coverage工具集提供了一系列工具和库&#xff0c;帮助开发者收集和分析代码覆盖率数据&#xff0c;从而评估测试的有效性和代码的执行情况。LLVM的Code Coverage工具集包括以下主要组件&#xff1a; Clang编译器&#xff1a;用于编译源代码并生成覆盖率信息…

Linux下配置Pytorch

1.Anaconda 1.1虚拟环境创建 2.Nvidia驱动 3.CUDA驱动安装 4.Pytorch安装 具体的步骤如上&#xff1a;可参考另一位博主的博客非常详细&#xff1a; Linux服务器配置PythonPyTorchCUDA深度学习环境_linux cuda环境配置-CSDN博客https://blog.csdn.net/NSJim/article/detai…

官方小游戏项目

一 项目原理&#xff1a;看广告&#xff0c;操作简单&#xff0c;时间自由&#xff0c;适合利用业余时间来做&#xff0c;一个广告大概在15s-30s之间。 二 介绍&#xff1a;给你开代理权限&#xff0c;你就有独立后台管理系统&#xff0c;监测每台手机每条广告的情况&#xff0…

探索Web3工具:正确使用区块链平台工具的秘诀

在当今日新月异的数字时代&#xff0c;区块链技术正以惊人的速度改变着我们的生活和工作方式。尤其对于那些想要踏入区块链世界的人来说&#xff0c;正确使用区块链平台工具至关重要。本文将向您介绍一些关键的Web3工具&#xff0c;并以TestnetX.com为例&#xff0c;展示如何利…