C++——list的实现以及源码

前言:

最近学习了c++list的实现,这让我对迭代器的理解又上升了一个新的高度,注意:代码里的list是放在一个叫zgw的命名空间里边,但是在实现list的代码中没有加namespace,这里给个注意,以后复习时能看懂。

list的节点:

我们首先需要搞出一个list的节点,来存储数据,以及节点的下一个指针,和节点的上一个指针,STL库里边这样设计的,所以我们这样设计:

	template< class T>struct ListNode{struct ListNode<T>* _next; struct ListNode<T>* _prev;T _data;ListNode(const T& data = T())   //这里给的是匿名对象:_next(nullptr),_prev(nullptr),_data(data) {};};

设计这个节点应该是没有什么难度的。
 

包装list类:
 

template<class T>
class list
{typedef ListNode<T> node;
public:list() :_head(new node()){_head->_next = _head;_head->_prev = _head;}
private:node* _head;
};

为了让我们的list更加具有可读性,我们将,ListNode<T>重命名为node。然后我们要给一个list的无参构造,相信到现在都能看得懂。

迭代器的设计:

前方高能!!!

然后就是上强度的地方,我们需要设计list的迭代器。我们先分析一下list指针与vector的指针又什么区别呢?

首先我们list存的是ListNode<T>节点的指针,每一个新的节点都是new出来的,而且是通过指针将他们链接起来,他在物理层面上就不是连续的。

所以当我们解引用就不是一个内置类型,或者是已经设计好的自定义类型,它就没有了重载运算符,因为我们的节点就是我们自己定义的自定义类型。就算是迭代器也必要支持重载运算符。

我们举一个例子
vector的例子,当我们要搞一个存储int数据的vector容器。

void Test4()
{vector<int> v1 = { 1,2,3,4 };vector<int>::iterator it1 = v1.begin();cout << *it1 << endl;
}

这里我们是存储int内置类型,当我们解引迭代器时,实际是解引用int类型的指针,然后也支持流插入,所以可以直接打印第一个元素。
 

我后我们再想我们如果要用vector存储一个我们自己搞的一个类型,是否还支持流插入呢?

class aa
{aa(int a=10,int b=20):_a(a),_b(b){}
private:int _a;int _b;
};void Test4()
{vector<aa> v1 ;aa a1;v1.push_back(a1);vector<aa>::iterator it1 = v1.begin();cout << *it1 << endl;
}

上面这个代码会不会报错呢?当然肯定会的,为什么呢?

就是因为我们没有重载流插入。

所以通过这个例子我们应该明白了,我们ListNode本生就是一个我们自己设计的类型,所以当我们在list返回ListNode指针再解引用时肯定是不行的,因为我们没有为ListNode设计重载运算符,但是我们在这有一个新的思路我们能不能再设计一个类,当做list的迭代器,这个类迭代器存ListNode类型的指针。

在第一次听到这个设计我也是很懵逼的,都是再通过我看了两遍教学课程,再加上自己研究了一个下午,终于是领悟了其中的原理,所以想好好理解还是得自己研究。

设计的源码:


namespace zgw
{//结构体定义template< class T>struct ListNode{struct ListNode<T>* _next;struct ListNode<T>* _prev;T _data;ListNode(const T& data = T()):_next(nullptr),_prev(nullptr),_data(data) {};};template <class T,class Ref,class Ptr>//我们设计三个参数的模板迭代器,以便我们返回对应的指针struct  ListIterator                  //和引用(在这里的我们需要设计const_iterator,以及{                                     //iterator两种类型的迭代器,然后我们再通过typedeftypedef ListNode<T> node;         //封装一下,提高可读性typedef ListIterator<T,Ref,Ptr> Self;ListIterator(node*head) :_node(head){};Ref operator*(){return _node->_data;}Ptr operator->(){return &_node->_data;}Self operator++(){_node = _node->_next;return *this;}Self operator++(int){Self re(_node);_node = _node->_next;return re;}bool operator==(const Self&node){return _node == node._node;}bool operator!=(const Self& node){return _node != node._node;}node* _node;};//封装链表,这只是一个头节点,当我们返回begin(),和end()时,//我们其实返回的是一个ListNode<T>类型的指针template<class T>class list{typedef ListNode<T> node;public:typedef ListIterator<T,T&,T*> iterator;typedef ListIterator<T, const T&, const T*> const_iterator;list() :_head(new node())     //当我们需要什么类型的迭代器就传对应的typedef{_head->_next = _head;_head->_prev = _head;}const_iterator cbegin(){return const_iterator(_head->_next);}const_iterator cend(){return const_iterator(_head);}iterator begin(){return iterator(_head->_next);}iterator end(){return iterator(_head);}void push_back(const T& data){node* newnode = new node(data);node* tail = _head->_prev;tail->_next = newnode;newnode->_prev = tail;newnode->_next = _head;_head->_prev = newnode;}iterator insert(iterator pos,const T&data){	node* cur = pos._node;node* newnode = new node(data);node* prev = cur->_prev;newnode->_next = cur;prev->_next = newnode;newnode->_prev = prev;cur->_prev = newnode;return iterator(newnode);} iterator erase(iterator pos){assert(pos != end()._node);node* cur = pos._node;node* prev=cur->_prev;node* next = cur->_next;prev->_next = next;next->_prev = prev;delete cur;return iterator(next);}void pop_back(){assert(_head != begin()._node);node* tail= end()._node->_prev;node* _head = end()._node;tail->_prev->_next = _head;_head->_prev = tail->_prev;delete tail;}void pop_front(){erase(begin());}void push_front(const T&data){insert(begin(), data);}private:node* _head;};
}

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

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

相关文章

解密论文评审过程:SCI论文是匿名送审的吗?

我是娜姐 迪娜学姐 &#xff0c;一个SCI医学期刊编辑&#xff0c;探索用AI工具提效论文写作和发表。 前几天有位学员问我&#xff0c;审稿人能看见我论文的作者和单位信息吗&#xff1f;应该是双方都匿名才更公平啊。 同行评议&#xff0c;在不同的期刊操作还真不一样。有双方…

spring学习笔记一

前言&#xff08;第一性原理&#xff09; 那么spring解决问题&#xff1a;JAVA开发框架&#xff0c;spring是用来解决企业级Java应用开发的问题。 通过依赖注入和面向切面编程来灵活管理和配置代码&#xff0c;提供一套工具来帮助开发和配置代码&#xff0c;提供了声明式事务…

AI重塑保险业未来:机器学习在风险评估、欺诈检测与客户服务中的深度应用

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

linux笔记6--shell相关

文章目录 1. 查看当前的shell类型2. ps -f命令3. 父子shell4. 分号在命令里的作用问题&#xff1a;环境变量echo&#xff1a; 5. sleep和jobssleep:jobs:例子&#xff1a;&: 6. 外部命令和内建命令图解外部命令type命令 7. history命令8. alias命令9. 推荐 1. 查看当前的sh…

Midjourney Describe API 使用文档

Midjourney Describe API 使用文档 Midjourney Describe API 的主要功能是通过上传图片&#xff0c;获取对图片的描述。使用该 API&#xff0c;只需要传递图片文件&#xff0c;API 会返回图片的详细描述。无需繁琐的参数设置&#xff0c;即可获得高质量的图片描述。 支持多种图…

银企直连业务

项目背景 不同银行的银企直连没有统一的标准&#xff0c;相同的输入输出。因此&#xff0c;用户需要根据每家银行提供的技术文档定制化地完成调用、获取数据。银企直连就是为了解决这些难题。引入银企直连后&#xff0c;企业的业务系统可与各家银行对接&#xff0c;实现标准化…

StarRocks与ClickHouse:简要快速选型对比

1. 引言 在大数据分析领域&#xff0c;高性能的数据库系统是关键。StarRocks和ClickHouse作为列式存储数据库的代表&#xff0c;各自具有独特的优势。本文将深入探讨它们的特性和适用场景&#xff0c;为选择合适的数据库系统提供参考。 2. StarRocks与ClickHouse简介 2.1 St…

华为机考入门python3--(30)牛客30-字符串合并处理

分类&#xff1a;字符串、进制转换 知识点&#xff1a; 获取偶数下标的字符 even_chars my_str[::2] 获取奇数下标的字符 odd_chars my_str[1::2]) 翻转字符串 reversed_str my_str[::-1] 二进制转十进制 num int(reversed_binary, 2) 十进制转十六进制 …

JDBC使用QreryRunner简化SQL查询注意事项

QreryRunner是Dbutils的核心类之一&#xff0c;它显著的简化了SQL查询&#xff0c;并与ResultSetHandler协同工作将使编码量大为减少。 注意事项 1. 使用QreryRunner必须保证实体类的变量名&#xff0c;和sql语句中要查找的字段名必须相同&#xff0c;否则查询 不到数据,会出…

Oracle中创建子查询临时表

1. 创建子查询临时表&#xff0c;当前会话中可以查询到&#xff0c;创建临时表需要指定表空间&#xff0c;不然会使用默认的表空间。 CREATE TABLE temp_table TABLESPACE tablespace_name AS SELECT id, number, count(1) AS total FROM example_table GROUP BY id, number…

Dubbo生态之nacos

1.Nacos简介 在博客Dubbo生态之初识dubbo协议-CSDN博客种我们已经介绍了为什么要使用注册中心&#xff0c;nacos作为注册中心的一种&#xff0c;相对于其它的主流注册中心有什么区别呢? NacosEurekaZookeeper数据存储存储在内存存储在内存存储在内存通信协议gRPChttpjute协议…

R实验 非参数性检验(二)

实验目的&#xff1a; 了解符号检验&#xff0c;掌握Wilcoxon符号秩检验&#xff0c;Wilcoxon秩和检验&#xff1b; 掌握Pearson拟合优度c2检验&#xff0c;Shapiro-Wilk 正态性检验&#xff0c;了解Kolmogorov-Smirnov单样本和双样本检验&#xff09;&#xff1b; 实验内容&…

06. Redis 主从复制

文章目录 概念主从复制主从复制的作用 环境配置总结复制原理 宕机后手动配置主机 概念 主从复制 是指将一台 Redis 服务器的数据复制到其他的 Redis 服务器&#xff1a; 前者称为主节点&#xff08;master/leader&#xff09;&#xff0c;后者成为从节点&#xff08;slave/f…

[数组查找]1.图解线性查找及其代码实现

线性查找 线性查找是一种在数组中查找数据的算法。与二分查找不同&#xff0c;即便数据没有按顺序存储&#xff0c;也可以应用线性查找。线性查找的操作很简单&#xff0c;只要在数组中从头开始依次往下查找即可。虽然存储的数据类型没有限制&#xff0c;但为了便于理解&#x…

windows 下nginx常用命令

1、启动&#xff0c;目录cmd&#xff0c;后 start nginx.exe 2.重新加载 nginx -s reload 3.查看状态 tasklist /fi “imagename eq nginx.exe” 4.关闭 nginx -s quit

头晕、心悸…你们小年轻配不上张俊杰的霸王茶姬,还要奔上市

近日&#xff0c;有多名网友在社交平台反映称&#xff0c;自己在喝了霸王茶姬的新品“万里木兰”奶茶后&#xff0c;出现了失眠、头晕、心悸等不同程度的不适症状&#xff0c;霸王茶姬方面则表示“可能是茶多酚过敏”。 而就在几天前举行的“2024年国际茶日现代东方茶创新论坛…

java —— 连接 MySQL 操作

MySQL 是独立于 java 之外的数据库&#xff0c;二者之间建立连接需要提前引入 mysql-connector-java 的 jar 包。 一、引入方法&#xff1a; ① 在项目中新建一个 Folder&#xff08;即文件夹&#xff09;&#xff0c;该文件夹通常命名为 lib&#xff0c;意思是存放项目所依赖…

揭秘 淘宝死店采集私信筛选,号称日赚500+

淘宝死店采集工具为电子商务创业者揭示了一个领域的新机遇&#xff0c;通过提供一系列深入分析和资源挖掘的功能&#xff0c;展现了从失败中寻找成功之道的独特方法论。以下是如何通过这种工具寻找电商平台中的隐含机会的几个关键方面&#xff1a; 分析失败的深层原因&#x…

一文搞懂 Transformer(总体架构 三种注意力层)

本文将从Transformer的本质、Transformer_的原理_、_Transformer的应用__三个方面&#xff0c;带您一文搞懂Transformer&#xff08;总体架构 & 三种注意力层&#xff09;。 节前&#xff0c;我们组织了一场算法岗技术&面试讨论会&#xff0c;邀请了一些互联网大厂朋友…

ISCC2024个人挑战赛WP-DLLCode

&#xff08;非官方解&#xff0c;以下内容均互联网收集的信息和个人思路&#xff0c;仅供学习参考&#xff09; 注意到程序调用了Encode函数对明文进行加密&#xff0c;点进去发现是对外部DLL的调用 静态分析DLL中的Encode函数可以得到 写出对应的解密脚本如下 #include <…