使用c++类模板和迭代器进行List模拟实现

List

  • 一、创建节点结构
  • 二、创建迭代器类
    • 1、类的结构
    • 2、一系列的运算符重载
  • 三、创建list
    • 1、细节把握
    • 2、迭代器函数
    • 3、构造函数和析构函数
    • 4、增删查改的成员函数

一、创建节点结构

template <class T>//节点结构
struct ListNode {ListNode<T>* _next;ListNode<T>* _prev;T  _val;ListNode(const T& x = T())//T()默认值:_next(nullptr),_prev (nullptr),_val (x){}
};

1、这里构建了模板类,使得节点支持多种数据类型和自定义类型
2、由于节点要被全局调用,所以这里写的是struct默认公有
3、给节点值赋值时,参数缺省值为T(),指的是c++11的默认赋值,内置类型使用默认值,如int为0,double为0.0,自定义类型给自定义默认值

二、创建迭代器类

由于迭代器在使用时,会面临可修改和不可修改的选择所以会有两种迭代器,所以这里可以选择传入参数进行调整

template <class T, class Ref, class Ptr>//模板参数,数值,引用,指针,方便构建多种类型的类
struct ListIterator

对迭代器封装使得用户在使用时,不用过多的繁琐操作,仅需使用一个迭代器就好,在内部typedef使得iterator有更强的适配性

  • 这里的成员变量仅有一个节点类型的指针。

1、类的结构

该类只是对迭代器的一系列封装,并实现一些运算符重载
成员变量仅用维护一个节点类型的指针

Node* _node;//成员变量仅维护一个指针

由于List是非顺序结构,所以迭代器的跳跃访问很重要:
对迭代器的顺序访问,需要借助operator++/–/*

2、一系列的运算符重载

        Ref operator*(){return _node->_val;}//it->Ptr operator->(){return &_node->_val;}//++itSelf& operator++(){_node = _node->_next;return *this;}//it++Self operator++(int){Self tmp(*this);_node = _node->_next;return tmp;}//--itSelf& operator--(){_node = _node->_prev;return *this;}//it--Self operator--(int){Self tmp(*this);_node = _node->_prev;return tmp;}bool operator!=(const Self& it){return _node != it._node;}bool operator==(const Self& it){return _node == it._node;}
  这里需要着重强调一下operator->

由于iterator是原生指针,所以对其直接解引用非常重要

class A {public:A(int a = 0,int b = 0 ):_a(a),_b(b){}int _a;int _b;};

如节点为A类型的结构体,想通过迭代器访问_a,_b,有两种方法
1、(*it)._a (*it)._b
这里相当于对指针解引用,拿到节点,对节点直接访问
2、it->_a it->_b

Ptr operator->(){return &_node->_val;}

这里重载的符号为->返回的节点val值的地址,按理在调用时it->为地址,则需要it->()->_a 但是编译器为了调用的直观性进行优化,仅需一个箭头即可

三、创建list

template<class T>class list

1、细节把握

  • 使用typedef将iterator统一起来
typedef ListIterator<T, T&, T*> iterator;
typedef ListIterator<T,const T&, const T*> const_iterator;
  • 成员变量为一个哨兵位的头节点和控制节点数量的统计_size,当然也可以通过遍历的方法现场统计
  • 链表示意可以参考链接: 双向循环链表

2、迭代器函数

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

两个版本的迭代器,自主挑选,由于前方处理过细节所以这里函数类型就不会冗余

3、构造函数和析构函数

1、 由于存在哨兵位头节点的创建,所以我们可以先写一个创建头节点的函数,方便后续构造函数调用

void empty_init(){_head = new Node;_head->_next = _head;_head->_prev = _head;_size = 0;}

让头节点的前后指针指向自己
2、 构造函数和赋值拷贝

        list()//无参数拷贝{empty_init();}list(const list<T>& lt)//对象拷贝构造{empty_init();for (auto& e : lt)push_back(e);}void swap(list<T>& lt){std:; swap(_head, lt._head);std:; swap(_size, lt._size);}list<T>& operator=(list<T> lt){swap(lt);return *this;}

值得注意的是拷贝构造的函数是一种现代写法,细节传实参拷贝,返回引用类型,等价于利用第二个构造函数,以对象构造了一个初始化的lt,然后交换lt与待拷贝的变量,并返回待拷贝的引用
在这里插入图片描述
3、 析构函数

        ~list(){clear();delete _head;_head = nullptr;}void clear(){iterator it = begin();while (it != end()){it = erase(it);}}

这里创建一个clear函数配合erase将节点统统释放,最后释放掉哨兵位头节点

4、增删查改的成员函数

这一部分关注好insert,erase函数就好,其他位置函数可直接附用

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

这里是基础的节点插入,如有疑问可查阅往期讲解

  • 链表示意可以参考链接: 双向循环链表
         iterator erase(iterator pos){Node* cur = pos._node;Node* next = cur->_next;Node* prev = cur->_prev;delete cur;prev->_next = next;next->_prev = prev;--_size;return iterator(next);}

值得注意的是,erase的返回值为删除位置的迭代器的下一个,因为一旦释放该位置,则会导致外面迭代器失效成为野指针,所以返回值向后走一个能避免访问冲突
其他的函数直接附用

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

最后进行几个简单测试:

void test1()
{list<int> lt;lt.push_back(1);lt.push_back(2);lt.push_back(3);lt.push_back(4);lt.push_back(5);auto it = lt.begin();while (it != lt.end()){cout << *it << " ";++it;}cout << endl;lt.erase(lt.begin());lt.erase(lt.begin());lt.insert((--lt.end()), 10);for (auto& e : lt){cout << e << " ";}cout << endl;
}

在这里插入图片描述

void test2()
{class A {public:A(int a = 0,int b = 0 ):_a(a),_b(b){}int _a;int _b;};A a(1, 1);//传参拷贝A b(a);//对象拷贝list<A> la;la.push_back(a);la.push_back(b);la.push_back({ 3,3 });//多参数拷贝la.push_back(A(1, 2));//匿名对象拷贝auto it = la.begin();while (it != la.end()){cout << it->_a << " "<<it->_b<<endl;++it;}}

在这里插入图片描述
这里有个小点,关于隐式类型转换,单参数拷贝时用(),多参数时用{}。

本期内容到此结束,感谢收看
在这里插入图片描述

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

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

相关文章

HTML_CSS学习:CSS的编写位置

一、位置_行内样式 相关代码&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>位置_行内样式</title> </head> <body> <!-- 行内样式&#xff1a;写在标签Style…

Springboot+vue+小程序+基于微信小程序的在线学习平台

一、项目介绍    基于Spring BootVue小程序的在线学习平台从实际情况出发&#xff0c;结合当前年轻人的学习环境喜好来开发。基于Spring BootVue小程序的在线学习平台在语言上使用Java语言进行开发&#xff0c;在数据库存储方面使用的MySQL数据库&#xff0c;开发工具是IDEA。…

APScheduler定时器使用:django中使用apscheduler,使用mysql做存储后端

一、基本环境 python版本&#xff1a;3.8.5 APScheduler3.10.4 Django3.2.7 djangorestframework3.15.1 SQLAlchemy2.0.29 PyMySQL1.1.0二、django基本设置 2.1、新增一个app 该app用来写apscheduler相关的代码 python manage.py startapp gs_scheduler 2.2、修改配置文件s…

关于NPM的Registry(npm源)

查看当前npm源&#xff1a; npm config get registry 官方源和淘宝源&#xff1a; ①官方源&#xff1a; npm config set registryhttp://registry.npmjs.org ②淘宝源&#xff1a; npm config set registryhttps://registry.npmmirror.com 据了解&#xff1a; 淘宝以前的源…

Ollamallama

Olllama 直接下载ollama程序&#xff0c;安装后可在cmd里直接运行大模型&#xff1b; llama 3 meta 开源的最新llama大模型&#xff1b; 下载运行 1 ollama ollama run llama3 2 github 下载仓库&#xff0c;需要linux环境&#xff0c;windows可使用wsl&#xff1b; 接…

C++浮点数format时的舍入问题

C浮点数format时的舍入问题 首先有这样一段代码&#xff1a; #include <iostream> #include <stdio.h> using namespace std;int main() {cout << " main begin : " << endl;printf("%.0f \r\n", 1.5);printf("%.0f \r\n&…

吴恩达2022机器学习专项课程(一)8.2 解决过拟合

目录 解决过拟合&#xff08;一&#xff09;&#xff1a;增加数据解决过拟合&#xff08;二&#xff09;&#xff1a;减少特征特征选择缺点 解决过拟合&#xff08;三&#xff09;&#xff1a;正则化总结 解决过拟合&#xff08;一&#xff09;&#xff1a;增加数据 收集更多训…

JAVA学习.Java异步任务.Futire与CompletableFuture

在Java中&#xff0c;异步任务可以通过Future和CompletableFuture来实现。 1. Future: - Future是在Java 5中引入的&#xff0c;用于表示异步计算的结果。 - 通过submit方法将任务提交给ExecutorService后&#xff0c;会返回一个Future对象&#xff0c;通过该对象可以获取任…

【c++】模板编程解密:C++中的特化、实例化和分离编译

&#x1f525;个人主页&#xff1a;Quitecoder &#x1f525;专栏&#xff1a;c笔记仓 朋友们大家好&#xff0c;本篇文章我们来学习模版的进阶部分 目录 1.非类型模版参数按需实例化 2.模版的特化函数模版特化函数模版的特化类模版全特化偏特化 3.分离编译模版分离编译 1.非类…

综合性练习(后端代码练习4)——图书管理系统

目录 一、准备工作 二、约定前后端交互接口 1、需求分析 2、接口定义 &#xff08;1&#xff09;登录接口 &#xff08;2&#xff09;图书列表接口 三、服务器代码 &#xff08;1&#xff09;创建一个UserController类&#xff0c;实现登录验证接口 &#xff…

网络应用层之(6)L2TP协议详解

网络应用层之(6)L2TP协议 Author: Once Day Date: 2024年5月1日 一位热衷于Linux学习和开发的菜鸟&#xff0c;试图谱写一场冒险之旅&#xff0c;也许终点只是一场白日梦… 漫漫长路&#xff0c;有人对你微笑过嘛… 全系列文档可参考专栏&#xff1a;通信网络技术_Once-Day的…

Apollo Dreamview+之播放离线数据包

前提条件 完成 Dreamview 插件安装&#xff0c;参见 Studio 插件安装 。 操作步骤 您可以通过包管理和源码两种方式快速体验离线数据包播放操作。其中进入 docker 环境和启动 dreamview 的命令有所区别&#xff0c;请您按照命令进行操作。 步骤一&#xff1a;启动并打开 Dr…

C++学习第十四课:运算符类型与运算符重载

C学习第十四课&#xff1a;运算符类型与运算符重载 在C中&#xff0c;运算符重载是一种使得自定义类型&#xff08;如类对象&#xff09;能够使用C内建运算符的能力。运算符重载允许程序员定义运算符对用户定义类型的特殊行为&#xff0c;这增加了程序的可读性和自然表达能力。…

PaLmTac嵌入软体手手掌的视触觉传感器

触觉是感知和操作之间的桥梁。触觉信息对于手部行为反馈和规划具有重要意义。软体手的柔性特性在人机交互、生物医学设备和假肢等方面具有潜在应用的优势。本文提出了一种名为 PaLmTac的嵌入软体手手掌的视触觉传感器&#xff08;vision-based tactile sensor, VBTS&#xff09…

学习mysql相关知识记录

执行一条select语句&#xff0c;期间发生了什么&#xff1f; MySQL的执行流程&#xff1a; 连接器 TCP连接 查询缓存 很鸡肋被取消 解析SQL 解析器 语法分析词法分析 执行SQL 预处理器 检查是否存在将 select * 中的 * 符号&#xff0c;扩展为表上的所有列 优化器 优化器主要…

LeetCode 198—— 打家劫舍

阅读目录 1. 题目2. 解题思路3. 代码实现 1. 题目 2. 解题思路 此题使用动态规划求解&#xff0c;假设 d p [ i ] [ 0 ] dp[i][0] dp[i][0] 代表不偷窃第 i i i 个房屋可以获得的最高金额&#xff0c;而 d p [ i ] [ 1 ] dp[i][1] dp[i][1] 代表偷窃第 i i i 个房屋可以获…

Bluetooth Profile 蓝牙协议栈总结

GAP-Generic Access Profile 控制设备广播和连接 GAP profile 的目的是描述&#xff1a; Profile rolesDiscoverability modes and proceduresConnection modes and proceduresSecurity modes and procedures 设备连接过程 LE中GAP有4种角色&#xff1a;BroadcasterObserv…

关于npm命令

我们知道nodejs安装后&#xff0c;用npm命令来安装管理依赖包&#xff0c;npm默认源是https://registry.npmjs.org 国内访问可能不够快&#xff0c;所以大家一般会配置使用国内镜像源&#xff0c;比如https://registry.npmmirror.com &#xff0c;这样可能访问和下载会快一些。…

RTMP 直播推流 Demo(二)—— 音频推流与视频推流

音视频编解码系列目录&#xff1a; Android 音视频基础知识 Android 音视频播放器 Demo&#xff08;一&#xff09;—— 视频解码与渲染 Android 音视频播放器 Demo&#xff08;二&#xff09;—— 音频解码与音视频同步 RTMP 直播推流 Demo&#xff08;一&#xff09;—— 项目…

vue计算属性是什么 怎么选择?

写在computed对象中的属性&#xff0c;本质上是一个方法&#xff0c;不过使用时依旧当属性来使用 虽然模板语法使用非常便利&#xff0c;但是它是被设计成用于简单运算的。在模板中放入太多的逻辑会让模板过重且难以维护 无论计算属性还是methos&#xff0c;这两种实现方式的…