C++学习之list的实现

在了解学习list实现之前我们首先了解一下关于迭代器的分类:

按功能分类:

正向迭代器    反向迭代器

const正向迭代器  const反向迭代器

按性质分类:

单向迭代器      只能++    例如单链表

 双向迭代器     可++,也可--     例如双链表 ,map和set

 随机迭代器     可加加,可减减,可+,可减  如顺序表(vector string),deque(双端队列)

这些都是由容器的底层实现。

可以看到库中提供的list模板是带头双向链表,故此我们实现它就大部分操作都是在数据结构中实现双链表时是一样。

目录

1.成员变量

节点类型的定义:

迭代器

重载前置++/--

重载后置++/--

重载*与->

重载!=与==

const迭代器

迭代器的优化:

 成员函数

构造函数

析构函数

拷贝构造函数

容量

size

修饰符 

insert()

erase ()

push_front()

pop_front()

push_back()

pop_back()

clear()


1.成员变量

template<class T>class mylist{typedef list_node<T>  Node;typedef _list_iterator<T, T&, T*> iterator;typedef _list_iterator<T,const T&,const T*> const_iterator;
private:Node* _head;size_t _size;
......
}

因为实现的是链表,故此我们的成员变量是链表头节点与链表的大小,这里我们还需要给出

节点类型的定义:

typedef list_node<T>  Node;
template<class T>struct list_node{//我们给一个缺省值为T的匿名对象list_node(const T& x = T()):data(x), _next(nullptr), _prev(nullptr){}T data;list_node<T>* _next;list_node<T>* _prev;};

迭代器

因为我们实现的是链表,那么迭代器的操作也就是链表节点的操作,节点并非一般的数据类型,

故此我们这里需要封装迭代器,迭代器本质是节点的地址:

//迭代器//认真思考的话,迭代器模拟的是一个双链表节点的运动,故我们封装迭代器里面一定是节点,这里放入节点的地址//并且重载对应的运算符,封装之后,我们在定义为iteratortemplate<class T>struct _list_iterator{//利用节点的指针实现迭代器typedef list_node<T>  Node;typedef _list_iterator<T> self;Node* _node;_list_iterator(Node* node):_node(node){}};

封装之后,我们还需要重载对于迭代器的运算符,这些重载都是在迭代器中的,我只是为了吧方法一个个体现出来,分开了。

重载前置++/--

       //重载加加self& operator++(){_node = _node->_next;return *this;}self& operator--(){_node = _node->_prev;return *this;}

重载后置++/--

       self& operator++(int){self tmp(*this);_node = _node->_next;return tmp;}self& operator--(int){self tmp(*this);_node = _node->prev;return tmp;}

重载*与->

        T* operator->(){return _node->data;}T& operator*(){return &_node->data;}

重载!=与==

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

const迭代器

const迭代器对应const对象,指的是内容不能被修改,而非指针(迭代器)本身,因此我们不能直接const iterator去认为是const迭代器,这样反而不能对迭代器进行++等操作,而需要我们去重新定义
 定义const迭代器,即内容是无法被修改,由于我们访问内容是通过解引用的方法故我们需要修改访问的的这两个操作符其引用返回为const,即内容无法被修改了。

template<class T>struct _list_const_iterator{typedef list_node<T>  Node;typedef _list_const_iterator<T> self;Node* _node;_list_const_iterator(Node* node):_node(node){}//迭代器的运算符重载self& operator++(){_node = _node->_next;return *this;}self& operator--(){_node = _node->prev;return *this;}self& operator++(int){self tmp(*this);_node = _node->_next;return tmp;}self& operator--(int){self tmp(*this);_node = _node->prev;return tmp;}const T* operator->(){return &_node->data;}const T& operator*(){return &_node->data;}bool operator!=(const self& s){return _node != s._node;}bool operator==(const self& s){return _node == s._node;}};

由于const迭代器与普通的迭代器区别也就在于访问的内容无法被修改,也就是修改*与->这两个访问内容的操作符,重新实现较为麻烦,库中实现是增加了两个模板参数Ref,Ptr,利用我们传的参数不一样,从而决定用的是哪一个迭代器。

迭代器的优化:

多出两个模板参数之后,我们的*与->返回类型就是到时候传入时候的类型,故这里我们直接用Ref与Ptr代替即可。

template<class T,class Ref,class Ptr>struct _list_iterator{//利用节点的指针实现迭代器typedef list_node<T>  Node;typedef _list_iterator<T,Ref,Ptr> self;Node* _node;_list_iterator(Node* node):_node(node){}
}
        Ptr operator->(){return _node->data;}Ref operator*(){return &_node->data;}

 在list中,对于模板迭代器我们传参不一样,决定了是const还是普通

typedef _list_iterator<T, T&, T*> iterator;
typedef _list_iterator<T,const T&,const T*> const_iterator;iterator begin(){//return iterator(_head->data);return _head->_next;}iterator end(){return _head;//哨兵位就是链表的结束标志}const_iterator begin()const{return _head->_next;}const_iterator end()const{return _head;}

 成员函数

构造函数

       //通过调用一个初始化链表的函数来实现构造函数mylist(){empty_init();}

析构函数

      //通过调用clear函数释放链表的节点,在释放哨兵位,即为析构~mylist(){clear();delete _head;_head = nullptr;}

拷贝构造函数

//拷贝构造,将节点尾插入新的对象mylist(mylist<T>& p){empty_init(p);for (auto it : p){push_back(it);}}

容量

size

size_t size(){return _size;}

修饰符 

insert()

在基于实现insert,我们的其他对list的操作都可以调用insert,不需要都去对链表节点一个个操作,

其次我们设计insert的返回值及参数位置都是迭代器,直接用。

iterator insert(iterator pos, const T x)//插入也会使迭代器失效,原位置节点找不到了{Node* cur = pos._node;Node* newnode = new Node(x);Node* prev = cur->_prev;//链接节点prev->_next = newnode;newnode->_prev = prev;newnode->_next = cur;cur->_prev = newnode;_size++;return iterator(newnode);}

erase ()

//删除   这里删除cur节点释放掉,会导致迭代器失效,因此要返回下一节点的迭代器iterator erase(iterator pos){Node* cur = pos._node;Node* prev = cur->_prev;Node* next = cur->_next;delete cur;prev->_next = next;next->_prev = prev;_size--;return iterator(next);}

push_front()

void push_front(){insert(begin());}

pop_front()

//头删void pop_front(){erase(begin());}

push_back()

//尾插void push_back(const T& x){//Node* tail = _head->preav;//Node* newnode = new Node(x);连接节点//tail->_next = newnode;//newnode->_next = _head;//newnode->prev = tail;//tail->_prev = newnode;//tail = newnode;//return (tail);insert(end(), x);}

pop_back()

//尾删void pop_back(){erase(end());}

clear()

//清数据void clear(){iterator it = begin();while (it != end()){it = erase(it);}}

至此我们对list的简单实现就完成了。

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

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

相关文章

荣耀崛起礼包码2023,荣耀崛起攻略

荣耀崛起是一款传统的卡牌手游&#xff0c;它将区域探索游戏无缝集成到卡牌游戏中。相信很多玩家都很好奇&#xff0c;荣耀崛起礼包码2023是什么&#xff1f;有没有给新手的一些攻略建议呢&#xff1f;下面小编就和大家简单分享一下&#xff0c;感兴趣的朋友&#xff0c;一起来…

【ARM CoreLink 系列 2 -- CCI-400 控制器简介】

文章目录 CCI-400 介绍DVM 机制介绍DVM 消息传输过程TOKEN 机制介绍 下篇文章&#xff1a;ARM CoreLink 系列 3 – CCI-550 控制器介绍 CCI-400 介绍 CCI&#xff08;Cache Coherent Interconnect&#xff09;是ARM 中 的Cache一致性控制器。 CCI-400 将 Interconnect 和coh…

Ajax + Promise复习简单小结simple

axios使用 先看看老朋友 axios axios是基于Ajaxpromise封装的 看一下他的简单使用 安装&#xff1a;npm install axios --save 引入&#xff1a;import axios from axios GitHub地址 基本使用 axios({url: http://hmajax.itheima.net/api/province}).then(function (result…

C语言学习系列-->字符函数和字符串函数

文章目录 一、字符函数1、字符分类函数2、字符转换函数 二、字符串函数1、strlen概述模拟实现 2、strcpy概述模拟实现 3、strcat概述模拟实现 3、strcmp概述模拟实现 4、有限制的字符串函数strncpystrncatstrncmp 4、strstr概述模拟实现 一、字符函数 1、字符分类函数 包含头…

vue3:22、vue-router的使用

import { createRouter, createWebHistory } from vue-router//history模式&#xff1a;createWebHistory //hash模式&#xff1a;createWebHashHistory//vite中的环境变量 import.meta.env.BASE_URL 就是vite.config.js中的base配置项 const router createRouter({history:…

Spring Data JPA:简化数据库交互的艺术

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f984; 博客首页——&#x1f405;&#x1f43e;猫头虎的博客&#x1f390; &#x1f433; 《面试题大全专栏》 &#x1f995; 文章图文…

lv3 嵌入式开发-11 Linux下GDB调试工具

目录 1 GDB简介 2 GDB基本命令 3 GDB调试程序 1 GDB简介 GDB是GNU开源组织发布的一个强大的Linux下的程序调试工具。 一般来说&#xff0c;GDB主要帮助你完成下面四个方面的功能&#xff1a; 1、启动你的程序&#xff0c;可以按照你的自定义的要求随心所欲的运行程序&#…

【leetcode 力扣刷题】回文串相关题目(KMP、动态规划)

回文串相关题目 5. 最长回文子串动态规划中心扩展算法 214. 最短回文串336. 回文对 5. 最长回文子串 题目链接&#xff1a;5. 最长回文子串 题目内容&#xff1a; 题目就是要我们找s中的回文子串&#xff0c;还要是最长的。其实想想&#xff0c;暴力求解也行……就是遍历所有的…

Python编程的八大魔法库

Python是一门广受欢迎的编程语言&#xff0c;其生态系统丰富多彩&#xff0c;拥有许多令人惊叹的依赖库&#xff0c;可以帮助程序员们在各种领域中创造出令人瞠目结舌的应用。在这篇文章中&#xff0c;我们将探讨Python编程的十大神奇依赖库&#xff0c;它们像魔法一样&#xf…

jmeter 计数器Counter

计数器可以用于生成动态的数值或字符串&#xff0c;以模拟不同的用户或数据。 计数器通常与用户线程组结合使用&#xff0c;以生成不同的变量值并在测试中应用。以下是计数器的几个常用属性&#xff1a; 变量前缀&#xff08;Variable Name Prefix&#xff09;&#xff1a;定义…

G. The Morning Star

Problem - G - Codeforces 思路&#xff1a;想了挺长时间的&#xff0c;一直没想到一个简便的方法在瞎搞。我们发现对于某个点来说&#xff0c;其他的点如果能够跟他匹配&#xff0c;那么一定在这8个方向上&#xff0c;而同时这8个方向其实对应这4条直线&#xff0c;假设点为(x…

静态路由 网络实验

静态路由 网络实验 拓扑图初步配置R1 ip 配置R2 ip 配置R3 ip 配置查看当前的路由表信息查看路由表信息配置静态路由测试 拓扑图 需求&#xff1a;实现 ip 192.168.1.1 到 192.168.2.1 的通信。 初步配置 R1 ip 配置 system-view sysname R1 undo info-center enable # 忽略…

C语言经典100例题(55)--从一个整数a中把从右端开始的4-7位取出来

目录 题目 问题分析 右移操作符 左移操作符 方法一 方法二 运行结果 题目 用c语言从一个整数a中把从右端开始的4-7位取出来 问题分析 右移操作符 右移操作符是一种位运算符&#xff0c;用于将二进制数向右移动指定的位数。它通常用符号" >> "表示…

W11下CMake MinGW配置OpenCV和Qt

&#x1f482; 个人主页:风间琉璃&#x1f91f; 版权: 本文由【风间琉璃】原创、在CSDN首发、需要转载请联系博主&#x1f4ac; 如果文章对你有帮助、欢迎关注、点赞、收藏(一键三连)和订阅专栏哦 前言 前几天将cuda版本的opencv给编译成功了&#xff0c;当时用的VS的MSVC&…

day37 线程

一、线程安全 二、多线程并发的安全问题 当多个线程并发操作同一临界资源 由于线程切换实际不确定 导致操作顺序出现混乱 产生的程序bug 严重时出现系统瘫痪 临界资源 &#xff1a;操作该资源的完整流程同一时间只能被单一线程操作的资源 多线程并发会出现的各种问题、 如…

聊聊 HTMX 吧

写在前面 最近看了几篇关于 htmx 的文章&#xff0c;自己也去看了一眼官网&#xff0c;也去油管看了一下当时 htmx 发布会的时候他们的演示&#xff0c;下面说几点我对这个所谓的新型起来的技术的看法&#xff0c; 他的来源是什么 首先说一下他虽然是一个新型的技术&#xff0c…

Java 多线程系列Ⅶ(线程安全集合类)

线程安全集合类 前言一、多线程使用线性表二、多线程使用栈和队列三、多线程下使用哈希表 前言 在数据结构中&#xff0c;我们学习过 Java 的内置集合&#xff0c;但是我们知道&#xff0c;我们学过的大多数集合类都是线程不安全的&#xff0c;少数如 Vector&#xff0c;Stack…

小程序分销机制介绍,小程序二级分销功能有哪些?

为什么有越来越多的用户选择使用小程序&#xff1f;跟“高大上”的APP相比&#xff0c;小程序不仅可以减少下载安装的复杂流程&#xff0c;还具备操作便捷、沉淀私域数据的优势。蚓链分销小程序具备裂变二维码、实时分佣、分销身份升级、层级分佣、商品个性化佣金设定等功能&am…

TortoiseSVN 详细操作指南

文章底部有个人公众号&#xff1a;热爱技术的小郑。主要分享开发知识、有兴趣的可以关注一下。为何分享&#xff1f; 踩过的坑没必要让别人在再踩&#xff0c;自己复盘也能加深记忆。利己利人、所谓双赢。 热爱技术的小郑 1、引言 考虑以下几种情况&#xff1a; 你是否在一个…

golang面试题:json包变量不加tag会怎么样?

问题 json包里使用的时候&#xff0c;结构体里的变量不加tag能不能正常转成json里的字段&#xff1f; 怎么答 如果变量首字母小写&#xff0c;则为private。无论如何不能转&#xff0c;因为取不到反射信息。如果变量首字母大写&#xff0c;则为public。 不加tag&#xff0c…