C++初阶:适合新手的手撕list(模拟实现list)

上次讲了常用的接口:今天就来进行模拟实现啦


文章目录

  • 1.基本结构与文件规划
  • 2.空参构造函数(constructor)
  • 3.完善迭代器(iterator)(begin(),end())
  • 4.List Capacity(size(),empty())
  • 4.增删改查(push_back,pop_back,pop_front,push_front,insert,erase)
  • 6.clear()和swap()
  • 7. 完善构造函数
    • 7.1list (size_type n, const value_type& val = value_type());
    • 7.2利用迭代器进行构造
    • 7.3拷贝构造
  • 8.重载=
  • 9.析构函数
  • 10.反向迭代器


1.基本结构与文件规划

请添加图片描述

  • list.h头文件:包含类的全部(函数的声明与定义)
  • reverseIterator.h文件:进行反向迭代器的实现
  • test.cpp源文件:进行调用test函数,测试和完善功能

基本结构:

#pragma once

namespace MyList
{
    // List的节点类
    template<class T>
    struct ListNode
    {
        ListNode<T>* _prev;
        ListNode<T>* _next;
        T _data;

        ListNode(const T& x=T())
            :_prev(nullptr)
            ,_next(nullptr)
            ,_data(x)
        {}
    };


    //List的迭代器类
    template<class T, class Ref, class Ptr>
    struct ListIterator
    {
        typedef ListNode<T> Node;
        typedef ListIterator<T, Ref, Ptr> Self;
        
        Node* _node;

        ListIterator(Node* node)//构造函数
            :_node(node)
        {}

        ListIterator(const Self& l)//拷贝构造函数
            :_node(l._node)
        {}

        T& operator*();
        T* operator->();
        Self& operator++();
        Self operator++(int);
        Self& operator--();
        Self& operator--(int);
        bool operator!=(const Self& l);
        bool operator==(const Self& l);

    };


    //list类
    template<class T>
    class list
    {
        typedef ListNode<T> Node;//默认是private 不给外面用
    public:
        typedef ListIterator<T, T&, T*> iterator;
        typedef ListIterator<T, const T&, const T&> const_iterator;
        //构造函数
        list();

        list(int n, const T& value = T());

        template <class Iterator>
        list(Iterator first, Iterator last);

        //析构
        ~list();


        // List Iterator
        iterator begin();
        iterator end();
        const_iterator begin();
        const_iterator end();


        // List Capacity
        size_t size()const;
        bool empty()const;


   
        // List Access
        T& front();
        const T& front()const;
        T& back();
        const T& back()const;


        // List Modify
        void 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);
        // 删除pos位置的节点,返回该节点的下一个位置
        iterator erase(iterator pos);
        void clear();
        void swap(list<T>& l);

    private:
        Node* _head;
    };
};
  1. ListNode 结构体:
    • 定义了链表的节点结构,包含了三个成员变量:前驱指针 _prev、后继指针 _next 和数据 _data
    • 构造函数初始化了这些成员变量,允许在创建节点时指定初始值。
  2. ListIterator 结构体:
    • 定义了链表的迭代器结构,包含了指向节点的指针 _node
    • 重载了一系列操作符,如 *->++--!===,以便于对链表进行遍历和操作。
  3. list 类:
    • 包含了迭代器的定义、构造函数、析构函数以及一系列的操作函数。
    • 定义了两种迭代器类型:iteratorconst_iterator,分别用于可修改的迭代和只读的迭代。
    • 实现了一系列的操作函数

2.空参构造函数(constructor)

        list()
        {
            _head = new Node;//去调用Node的默认构造函数了
            _head->_next = _head;
            _head->_prev = _head;//带头双向循环链表是这样的
        }

使用new:动态开辟+调用默认构造函数了


3.完善迭代器(iterator)(begin(),end())

这里为什么我们要把迭代器封装为一个类呢?明明之前模拟vectorstring时,就直接typedef

之前模拟vectorstring时,二者底层都是连续的,想要到下一个可以直接++;想要得到里面的数据可以直接*

但是现在对于list是不行的,我们就需要重载各种运算符,但是底层又是一个指针(内置类型)不能重载,现在就只能封装进一个类里,就能重载

 //List的迭代器类
    template<class T, class Ref, class Ptr>
    struct ListIterator
    {
        typedef ListNode<T> Node;
        typedef ListIterator<T, Ref, Ptr> Self;
        
        Node* _node;

        ListIterator(Node* node)//构造函数
            :_node(node)
        {}

        ListIterator(const Self& l)//拷贝构造函数
            :_node(l._node)//这里是浅拷贝(写不写都行)
            //新创建的迭代器和原迭代器指向相同的内存地址
        {}

        Ref operator*()
        {
            return _node->_data;
        }
        Ptr operator->()
        {
            return &(_node->_data);
        }

        Self& operator++()//前置
        {
            _node = _node->_next;//自己要更新
            return *this;
        }
        Self operator++(int)
        {
            Self s(*this);
            _node = _node->_next;
            return s;
        }

        Self& operator--()
        {
            _node = _node->_prev;//自己要更新
            return *this;
        }
        Self& operator--(int)
        {
            Self s(*this);
            _node = _node->_prev;
            return s;
        }

        bool operator!=(const Self& l)
        {
            return _node != l._node;
        }
        bool operator==(const Self& l)
        {
            return _node == l._node;
        }

    };


    //list类
    template<class T>
    class list
    {
        typedef ListNode<T> Node;//默认是private 不给外面用
    public:
        typedef ListIterator<T, T&, T*> iterator;
        typedef ListIterator<T, const T&, const T&> const_iterator;
        //构造函数
        list()
        {
            _head = new Node;//去调用Node的默认构造函数了
            _head->_next = _head;
            _head->_prev = _head;//带头双向循环链表是这样的
        }

        // List Iterator
        iterator begin()
        {
            return _head->_next;//隐式类型转换(由单参构造函数支撑)
        }
        iterator end()
        {
            return _head;
        }
        const_iterator begin()
        {
            return _head->_next;
        }
        const_iterator end()
        {
            return _head;
        }

    private:
        Node* _head;
    };
};

4.List Capacity(size(),empty())

       // List Capacity
        size_t size()const
        {
            size_t size = 0;
            iterator it = begin();
            while (it != end())
            {
                size++;
                ++it;
            }
            return size;
        }
        bool empty()const
        {
            return size() == 0;
        }

4.增删改查(push_back,pop_back,pop_front,push_front,insert,erase)

        // List Modify
        void 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)
        {
            Node* cur = pos._node;
            Node* prev = pos._node->_prev;
            Node* newnode = new Node(val);//创建新节点

            newnode->_next = cur;
            cur->_prev = newnode;
            newnode->_prev = prev;
            prev->_next = cur;

            return newnode;//隐式类型转换
        }
        // 删除pos位置的节点,返回该节点的下一个位置
        iterator erase(iterator pos)
        {
            assert(pos != _head);

            Node* cur = pos._node;
            Node* prev = cur->_prev;
            Node* next = cur->_next;

            prev->_next = next;
            next->_prev = prev;

            delete cur;
            return next;
        }

使用test1函数看功能是否正常

	void print(MyList::list<int>& lt)
	{
		list<int>::iterator it = lt.begin();
		while (it != lt.end())
		{
			cout << *it << " ";
			++it; // 更新迭代器
		}
		cout << endl;
	}

	void test1()
	{
		MyList::list<int> lt;
		lt.push_back(1);
		lt.push_back(2);//尾插2个
		print(lt);

		lt.pop_back();//尾删一个
		print(lt);

		lt.push_front(0);//头插一个
		print(lt);

		lt.pop_front();//头删一个
		print(lt);
	}

请添加图片描述


6.clear()和swap()

       void clear()
        {
            //删除除头结点(_head)以外的节点
            iterator it = begin();
            while (it != end())
            {
                it = erase(it);
            }
        }

        void swap(list<T>& l)
        {
            std::swap(_head, l._head);
        }

7. 完善构造函数

7.1list (size_type n, const value_type& val = value_type());

list(int n, const T& value = T())
{
    _head = new Node;
    _head->_next = _head;
    _head->_prev = _head;

    for (int i = 0; i < n; ++i)
    {
        push_back(value);
    }
}

请添加图片描述

7.2利用迭代器进行构造

	    template <class Iterator>
        list(Iterator first, Iterator last)
        {
            while (first != last)
            {
                push_back(*first);
                first++;
            }
        }

为什么使用模版:

因为可能使用其他类型的迭代器来进行初始化

7.3拷贝构造

        list(const list<T>& lt)
        {
            _head = new Node;
            _head->_next = _head;
            _head->_prev = _head;

            iterator it = copy.begin();
            while (it != copy.end())
            {
                push_back(*it);
                it++;
            }
        }

8.重载=

        list<T>& lt operator=(list<T> lt)
        {
            swap(lt);
            return *this;
        }

注意这里的参数不是常量引用,而是按值传递的。这是因为在赋值操作符中我们会调用 swap 函数,按值传递可以保证传入的参数会被复制一份,避免对原对象的修改。在函数体内,我们调用了 swap 函数,将当前对象和传入的对象进行内容交换,然后返回 *this,即当前对象的引用。


9.析构函数

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

调用clear函数后,就只剩下头结点了


10.反向迭代器

我们再次使用封装的思想,封装一个反向迭代器进去

#pragma once

template <class iterator,class Ref,class Pre>
struct reserve_iterator
{
	typedef reserve_iterator<iterator, Ref, Pre> self;

	iterator _it;

	reserve_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*()
	{
		iterator tmp = _it;
		--tmp;
		return *tmp;
	}

	Pre operator->()
	{
		return &(operator*());
	}

	bool operator!=(const self& s)
	{
		return _it != s._it;
	}
	bool operator==(const self& s)
	{
		return _it == s._it;
	}

};

此时那list类里就是这样:

请添加图片描述


好啦,list的内容也结束啦,下次就是Stack和Queue了。感谢大家支持!!!

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

MySQL篇----第二十二篇

系列文章目录 文章目录 系列文章目录前言一、什么是表级锁二、什么是页级锁三、什么是行级锁四、什么是悲观锁前言 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站,这篇文章男女通用,看懂了就去分享给你的码吧。 一、…

vue axios 请求后端无法传参问题

vue请求后端无法传参问题 问题描述处理过程总结 问题描述 在学习vue时&#xff0c;使用axios调用后端&#xff0c;发现无法把参数正确传到后端&#xff0c;现象如下&#xff1a; 使用vue发起请求&#xff0c;浏览器上已经有传参&#xff0c;但是后端没接收到对应的用户名密码&…

SpringCloud之Nacos用法笔记

SpringCloud之Nacos注册中心 Nacos注册中心nacos启动服务注册到Nacosnacos服务分级模型NacosRule负载均衡策略根据集群负载均衡加权负载均衡Nacos环境隔离-namespace Nacos与eureka的对比临时实例与非临时实例设置 Nacos配置管理统一配置管理微服务配置拉取配置自动刷新远端配置…

STM32CubeMX的下载和安装固件库详细步骤

年也过了&#xff0c;节也过了&#xff0c;接下来又要进入紧张的学习中来了。过完年后发现一个问题&#xff0c;就是我之前吃的降压药不太管用&#xff0c;每天的血压只降到了91/140左右&#xff0c;没有到安全范围内&#xff0c;从初三开始换了一种降压药&#xff0c;效果出奇…

Java完整版宿舍管理

项目技术&#xff1a; springboot layui idea mysql5.7 jdk1.8 maven3 有需要该项目的小伙伴可以私信我你的Q。 功能描述&#xff1a; &#xff08;1&#xff09;基本信息管理 基本信息分为学生信息和宿舍信息两部分&#xff0c;其功能是负责维护这些信息&#xff0c…

MySQL学习记录——팔 函数

文章目录 1、日期函数2、字符串函数3、数学函数4、其它函数 1、日期函数 //获取日期 select current_date(); //获取时间 select current_time(); //获取时间戳, 格式为日期时间 select current_timestamp(); //获取当前时间, 格式为日期时间 select now(); //获取参数的日期部…

p图考试,搜题软件哪个好?如何利用大学搜题工具查找特定学科的题目? #微信#知识分享

大学生必备&#xff0c;这条笔记大数据一定定要推给刚上大学的学弟学妹&#xff01;&#xff01; 1.三羊搜题 这是个微信公众号 是个公众号&#xff0c;支持文字、语音、截图搜题&#xff0c;截图搜题&#xff0c;同时也支持相似题查看及解析&#xff0c;因为可以在电脑上使…

全战三国与Amadey病毒

一、疑云 某天晚&#xff0c;本人突发奇想从外网上体验一下全战三国&#xff08;全面战争&#xff1a;三国&#xff09;的可玩版本&#xff08;没玩过全战系列&#xff0c;听说三国版是个半成品&#xff0c;打算先体验一下再考虑入正版。国内网站找到的资源&#xff1a;1.53的…

python-分享篇-自定义词云图颜色

文章目录 准备代码效果 准备 运行本程序需要安装第三方模块matplotlib、jieba、wordcloud 和scipy &#xff0c;scipy要求1.0.0版本&#xff0c;否则程序将出现导入错误&#xff1a;ImportError: cannot import name ‘imread’ from ‘scipy.misc’ ImportError: cannot impo…

【动态规划】【C++算法】1563 石子游戏 V

作者推荐 【数位dp】【动态规划】【状态压缩】【推荐】1012. 至少有 1 位重复的数字 本文涉及知识点 动态规划汇总 LeetCoce:1563 石子游戏 V 几块石子 排成一行 &#xff0c;每块石子都有一个关联值&#xff0c;关联值为整数&#xff0c;由数组 stoneValue 给出。 游戏中…

倒模UV树脂胶制作舞台监听耳返入耳式耳机壳可行吗?

使用倒模UV树脂胶制作舞台监听耳返入耳式耳机壳是一种可行的方法&#xff0c;能够为专业或业余的音乐制作人、DJ和舞台表演者提供定制的、高品质的监听耳返体验。 以下是一些关键步骤和注意事项&#xff1a; 耳模制作&#xff1a;首先&#xff0c;为使用者制作一个精确的耳模…

无人机地面站技术,无人机地面站理论基础详解

地面站作为整个无人机系统的作战指挥中心&#xff0c;其控制内容包括:飞行器的飞行过程&#xff0c;飞行航迹&#xff0c; 有效载荷的任务功能&#xff0c;通讯链路的正常工作&#xff0c;以及 飞行器的发射和回收。 无人机地面站总述 地面站作为整个无人机系统的作战指挥中心…

【教学类-19-07】20240214《ABAB式-规律黏贴18格-手工纸15*15CM-一页一种图案,A空,竖向、有边框》(中班)

背景需求 利用15*15CM手工纸制作AB色块手环&#xff08;手工纸自带色彩&#xff09; 素材准备 代码展示 作者&#xff1a;阿夏 时间&#xff1a;2024年2月14日 名称&#xff1a;正方形数字卡片AB图案 _ 华光彩云_CNKI A的位置有图案 A空18格 AB向 一页一种图案&#xff0c;…

[CTF]-PWN:C++文件更换libc方法(WSL)

C文件与C文件更换libc有很多不一样的地方&#xff0c;我是在写buu的ciscn_2019_final_3才意识到这个问题&#xff0c;C文件只需要更换libc和ld就可以了&#xff0c;但是C文件不同&#xff0c;除了更换libc和ld&#xff0c;它还需要更换libstdc.so.6和libgcc_s.so.1 更换libc和…

FPGA_简单工程_拨码开关

一 框图 二 波形图 三 代码 3.1 工程代码 module bomakiaguan (input [15:0] switch, // 输入16路拨码开关output reg [15:0] led // 输出16个LED灯 );always (switch) beginled < switch; // 将拨码开关的值直接赋给LED灯 end // 将拨码开关的值直接赋给LED灯 endmodu…

【知识整理】产研中心岗位评定标准之大数据岗位

为贯彻执行集团数字化转型的需要,该知识库将公示集团组织内各产研团队不同角色成员的职务“职级”岗位的评定标准; 一、定级定档目的 通过对公司现有岗位及相应岗位员工的工作能力、工作水平进行客观公正评定,确定各岗位的等级及同等级岗位员工对应的档级,从而为员工以后的晋升…

多模态基础--- word Embedding

1 word Embedding 原始的单词编码方式&#xff1a; one-hot&#xff0c;维度太大&#xff0c;不同单词之间相互独立&#xff0c;没有远近关系区分。 wordclass&#xff0c;将同一类单词编码在一起&#xff0c;此时丢失了类别和类别间的相关信息&#xff0c;比如class1和class3…

C++类和对象-多态->案例1计算器类、案例2制作饮品、案例3电脑组装需求分析和电脑组装具体实现

#include<iostream> using namespace std; #include<string> //分别利用普通写法和多态技术实现计算器 //普通写法 class Calculator { public: int getResult(string oper) { if (oper "") { return m_Num1 m_Num2; …

Leetcode-107. 二叉树的层序遍历 II

今天的每日一题&#xff0c;和昨天比&#xff0c;就加了五个字母...... 给你二叉树的根节点 root &#xff0c;返回其节点值 自底向上的层序遍历 。 &#xff08;即按从叶子节点所在层到根节点所在的层&#xff0c;逐层从左向右遍历&#xff09; 示例 1&#xff1a; 输入&…

【硬核】堪称全地球最好用并且免费的AI人工智能图片无损放大神器,我只偷偷告诉你一个人!

&#x1f680; 个人主页 极客小俊 ✍&#x1f3fb; 作者简介&#xff1a;web开发者、设计师、技术分享博主 &#x1f40b; 希望大家多多支持一下, 我们一起学习和进步&#xff01;&#x1f604; &#x1f3c5; 如果文章对你有帮助的话&#xff0c;欢迎评论 &#x1f4ac;点赞&a…
最新文章