STL模拟实现1.0 -- list和iterator模拟实现和简单分析
引言
C ++ 标准模本库《STL》中有很多优秀的代码实现,不然怎么能叫做C++标准模板库呢,其中一个实现就是有一个容器,叫做list。所谓容器其实就是存储相同类型数据的一个存储集合,list的底层实现其实就是一个链表。
我们的普通数组在使用的时候可以定义一个指针指向一个节点,然后使指针 ++ 就可以访问下一个节点,我们想要我们的list也能够使用这个功能,于是就出现了迭代器iterator,通过定义一个iterator,我们可以使用++访问下一个节点,也可以使用*来找到iterator维护的节点的数据,本文就简单模拟实现iterator。
iterator的使用
看下面的代码
#include<iostream>
using namespace std;
#include<list>
void TestList()
{list<int> l;l.push_back(1);l.push_back(2);l.push_back(3);list<int>::iterator it = l.begin();while (it != l.end()){cout << *it << endl;it++;}
}
这个时候输出的结果就是1 2 3,这样使用list访问数据是不是很方便呢,从上面的使用中,我们也可以终于理解迭代器到底是什么东西了,迭代器其实就是一个维护list中各个节点的一个东西,其中的begin()和end()放回的也是一个迭代器,那么迭代器到底是一个什么东西呢 ,我们呢通过下面的下面的模拟实现可以知道迭代器到底是什么。
简单模拟实现list和iterator
talk is cheap,show you this code!
代码的后面是模拟实现的分析哦,那才是重点
#include<iostream>
using namespace std;#include<list>template<class T>
struct _ListNode
{_ListNode<T>* _next;_ListNode<T>* _prev;T _data;_ListNode(T d = 0):_data(d), _next(NULL), _prev(NULL){}
};//这个T标记的是维护的数据类型,Ref是引用,这个引用是对数据的引用,就是这里面data的引用
//Ptr是指针,这个指针也是指向数据data的指针这两个模板类型我们可以在
//List这个类里面使用,因为在编写List_Iterator的时候,会经常使用Ref和Ptr
template<class T, class Ref, class Ptr>
class List_Iterator
{typedef _ListNode<T> Node;typedef List_Iterator<T, Ref, Ptr> Self; //这个地方写的 有点失误了,就是把List_Iterator的单词写错了
public:List_Iterator(Node* it):_it(it){}List_Iterator(const List_Iterator& it){_it = it._it;}//List_Iterator& operator++()Self& operator++(){_it = _it->_next;return *this;}Self operator++(int) //注意前置++返回的是引用,而后置++返回的是Self{Node* tmp = _it;_it = _it->_next;return tmp;}//T& operator*()Ref operator*(){return _it->_data;}bool operator!=(const Self& it){return _it != it._it; //一开始写这个!=运算符重载的时候也出现了错误,就是我一开始是拿this和&it比//这显然是不对的,两个迭代器不可能是相等的,所以这个时候应该是拿//这两个迭代器所指向的内容来比较,就是那他们的成员变量来 进行比较才正确}private:Node* _it;
};//这里我们需要做一个修改就是,我们需要把这个链表定义成一个双向的有头的循环链表
//这个工作就是要在构造函数的时候去做了
//所以这里_tail就不需要了//有头结点,_head维护了一个头结点,但是这个节点不放置任何的数据,因为这样在后面的增删查改的时候使用的时候会非常的方便
template<class T>
class _List
{typedef _ListNode<T> Node;public:typedef List_Iterator<T, T&, T*> Iterator;//const迭代器typedef List_Iterator<T, const T&, const T*> Const_Iterator;//这里为什么传递的是const T&和const T*呢,如果我们这样传递的话,在实现iterator的时候,里面有个函数是//Ref operator*()//{// return _it->_data;//}//这个时候因为我们传递的是const的,所以这个时候的Ref就成了const的了,这个时候的iterator所维护的节点//里面的数据就不可改变了,这就实现了const的作用了//这也是我们为什么设计iterator的时候为什么设计的是三个模板参数了//配合着const_iterator我们还需要模拟实现返回时const的end和begin_List():_head(new Node) //这里报了一个错误就是,没有合适的构造函数可以使用,原因是我上面写_ListNode的时候//传参了,但是这里没有传参数,所以我上面的额时候把传递的参数默认为d = 0这个时候就算是我不传递参数//也没有什么问题了// , _tail(_head){_head->_next = _head;_head->_prev = _head;}//这里开始设计的时候想着返回值是Node*,然后外面使用的时候,可以拿这个Node*构造一个迭代器,调用迭代器//的构造函数,就生成了一个迭代器了,但是问题在于,这样做的话,是不是会引起我们的某些时候的一些误解呢//我们可以直接返回的就是一个迭代器//但是如果我们这里设计的返回值是一个迭代器的话,我们需要在这个函数里面定义一个迭代器,然后返回的时候//生成一个临时的对象,返回给外层的迭代器,这个时候调用了一次拷贝构造函数,返回给外层的迭代器的时候//又调用了一次拷贝构造函数,这样会不会太复杂了呢//Iterator begin()//{// if (_head->_next == _head) //这里做了一个判断就是当我们的链表是空的时候,就不能直接返回next// {// return Iterator(NULL);// }// return Iterator(_head->_next);//}//在使用Const_Iterator的时候出现了问题就是,为什么我下面的普通的begin函数不注释 的时候,就编译不过呢Const_Iterator begin() const //返回值是Const_iterator为了和上面的匹配,然后后面 的一个const为为了//this指针所指向的内容{if (_head->_next == _head) //这里做了一个判断就是当我们的链表是空的时候,就不能直接返回next{return Const_Iterator(NULL);}return Const_Iterator(_head->_next);}//Node* begin() const//{// if (_head->_next == _head) //这里做了一个判断就是当我们的链表是空的时候,就不能直接返回next// {// return NULL;// }// return _head->_next;//}/*Iterator end(){if (_head->_next == _head){return Iterator(NULL);}return Iterator(_head);}*/Const_Iterator end() const{if (_head->_next == _head){return Const_Iterator(NULL);}return Const_Iterator(_head);}void Pushback(const T& data){Node* cur = new Node(data);cur->_prev = _head->_prev;_head->_prev = cur;cur->_next = _head;cur->_prev->_next = cur;}//void Print()//{// Const_Iterator it = begin(); //这个原因是,我们的begin不是 const的,但是我们的const_ietator是const的// while (it != end())// {// cout << *it << endl;// it++;// }//}
private:Node* _head;
// Node* _tail;
};void TestList()
{_List<int> l;l.Pushback(0); //一开始的时候,我这里设置的这个参数是引用,这个时候是辩不过的,因为我们的0是在常量区的l.Pushback(1);l.Pushback(2);_List<int>::Const_Iterator it = l.begin();/*_List<int>::Ierator it = l.begin();cout << *it << endl;++it;cout << *it << endl;++it;cout << *it << endl;*/
// _List<int>::Iterator it = l.begin();//*it = 5;//l.Print();/*list<int> l;l.push_back(0);l.push_back(1);list<int>::const_iterator it = l.begin();*/}
这里我们按照一个类一个类的来进行分析
_ListNode
首先看到的是_ListNode,这里就不用过多的解释什么了,成员变量是两个指向该节点的指针,和一个存放数据的东西;这里只实现了一个构造函数,构造函数完成的初始化的时候把两个指针置为NULL
List_Iterator
- 构造函数,我们的List_Iterator底层维护的实际上就是一个指针,指向的就是我们的链表的一个个节点,构造函数中是把一个指针给了迭代器中,这个 函数一般在我们的list中的begin函数中使用到
- 拷贝构造函数。这个是拿一个已经存在的迭代器去初始化 另一个迭代器。
前置 ++ 和后置 ++ 。Self& operator++()和Self operator++(int)
这两个函数是我们实现的重点,因为我们 开始写迭代器 的目的就是希望能够像一个指针使用它,直接使用 ++ 操作就可以实现对下一个节点的访问了。这里我们 应该注意的一个问题就是,我们的前置 ++ 和后置 ++ 的返回值应该是不一样的,因为前置++返回的是先 ++ 后返回,返回的是 ++ 后的结果,所以这个时候应该返回的是Self的引用,但是后置 ++ 是先返回在 ++ ,这个时候是不能够使用引用的,因为我们既然要先返回再 ++ ,我们就要在 函数的里面先将 ++ 前的值记录下来,所以这个时候就需要定义一个变量保存 ++ 前的结果,然后返回,如果这个时候使用的是引用的话,我们的函数结束之后,刚刚的变量就释放掉了,所以这个时候的引用是错误的。*号运算符的重载。这个重载返回的应该是迭代器 所指向的数据,所以这个时候返回的是T&,然后我们又在上面对这个T&进行了一个封装,所以这个时候返回值直接使用Ref。
_List
关于链表,我们这里维护的是一个双向的循环的有头节点的双向链表
typedef List_Iterator<T, T&, T*> Iterator;//const迭代器typedef List_Iterator<T, const T&, const T*> Const_Iterator;
这里重点说明一下,为什么要将迭代器声明为三个模板参数的,这里就可以很好的体现了复用性了
这里为什么传递的是const T&和const T*呢,如果我们这样传递的话,在实现iterator的时候,里面有个函数是
Ref operator*()
{
return _it->_data;
}
这个时候因为我们传递的是const的,所以这个时候的Ref就成了const的了,这个时候的iterator所维护的节点
里面的数据就不可改变了,这就实现了const的作用了
这也是我们为什么设计iterator的时候为什么设计的是三个模板参数了
配合着const_iterator我们还需要模拟实现返回时const的end和begin
相关文章:

hdc mfc 画扇形图_使用echarts绘制条形图和扇形图
使用echarts绘制条形图和扇形图简单举例说明下echarts如何绘制条形图和扇形图代码示例echarts绘制条形图和扇形图var mychart1echarts.init(document.getElementById(chart1),light);// 指定图表的配置项和数据var option {title: {text: ECharts 入门示例},tooltip: {},legen…
Java学习笔记45:Java 线程与主线程之间的变量关系
运行下面的代码: package com.test.www;public class Test {public static int count 0;public static void inc() {//这里延迟1毫秒,使得结果明显try {Thread.sleep(1);} catch (InterruptedException e) {}count;}public static void main(String[] a…

会计的思考(36):会计--企业运营的数码相机
新浪网上看到一张图片,拍的是2009年12月3日的武汉市,整个城市在笼罩在一片污浊空气当中,让人震惊。虽然我在深圳,但也清楚好不到哪去。 震惊之余,感谢新浪,更感谢某位摄影师,让我们了解到生活的…

spring cloud连载第二篇之Spring Cloud Config
Spring Cloud Config Spring Cloud Config为分布式服务提供了服务侧和客户侧的外部配置支持。通过Spring Cloud Config你可以有一个统一的地方来管理所有应用的外部配置。 默认服务端存储实现用的是git,因此,它很容易支持配置环境的标签版本,…

CodeOnly
关于设置[Key]标志 要先添加 程序集的引用 在添加 using System.ComponentModel.DataAnnotations; 命名控件转载于:https://www.cnblogs.com/since87/archive/2013/06/09/3129399.html

继承和多态 2.0 -- 继承的六个默认成员函数
本文重要介绍普通继承中如何写派生类的六个默认成员函数,主要是针对在派生类中,如何调用基类的六个默认成员函数 需要说明的一点就是,如果子类中没有调用父类的函数时,系统会自动生成一个。 构造函数 子类中有父类的成员&#…

js 让鼠标右下角有一排小字_js布局中一排大字下面接着一排小字怎么打出来?...
展开全部可以使用 TypewriterJS 实现效果是这样 ,百度图片少了一部分,可以通过链接32313133353236313431303231363533e78988e69d8331333433626438看效果链接: 网页链接示例在这里: 网页链接文档地址: 网页链接使用步骤引用 TypewriterJShtmlcss.article__title {fon…

Hudson神奇的环境变量
Hudson神奇的环境变量 http://blog.sina.com.cn/s/blog_798f21a00100z6zw.html转载于:https://blog.51cto.com/myloveworld/950156

英语影视台词---四、Sideways
英语影视台词---四、Sideways 一、总结 一句话总结:杯酒人生 Sideways,大致意思是“偏离、倾斜、转向…”。很明显中文译名与英文原名并没有什么关联,《杯酒人生》这个名字,其实也可以译为《并肩前行》,应该是从影片内…

继承和多态 3.0 -- 菱形继承
单继承和多继承 C的继承方式是支持单继承和多继承的,首先看一下代码,分清单继承和多继承 单继承 class A { public:int _a; };class B :public A { public:int _b; };class C : public A { public:int _c; }; 类似于上面的方式就是单继承,或…

C# split 几种使用方法
第一种方法: string s "abcdeabcdeabcde"; string[] sArray s.Split(c); foreach (string i in sArray) Console.WriteLine(i.ToString()); Console.ReadKey();输出下面的结果:abdeabdeabd…

模糊匹配 读音_onenote搜索机制详解②:两种搜索模式,模糊与精确匹配
先从纯文本搜索讲起,这是最基本也是最重要的。从这篇开始,以及接下来连续几篇文章,都会介绍搜索的基础功能。注意,这几篇文章中谈论的都是基本的、正常的搜索功能,暂时不考虑Bug等因素。在很多软件(例如wor…

EXT3与EXT4的主要区别
Linux kernel 自 2.6.28 开始正式支持新的文件系统 Ext4。 Ext4 是 Ext3 的改进版,修改了 Ext3 中部分重要的数据结构,而不仅仅像 Ext3 对 Ext2 那样,只是增加了一个日志功能而已。Ext4 可以提供更佳的性能和可靠性,还有更为丰富的…

Java IO 4 : RandomAccessFile
RandomAccessFile: 认识:java输入/输出流体系中功能最丰富的文件内容访问类 既可以读取文件内容,也可以向文件传输数据,并且支持“随机访问“的方式,程序可以跳转到任意地方来读写数据。 特点:与OutputStream/Writ…

二叉树 1.0 -- 创建二叉树、遍历二叉树、二叉树常见问题求解
树的结构主要是为了查找,这个主要是为了搜索,树的结构关注的不是增删查改 树 广义上面的树的结构我们不知道树的一个节点是有几个子节点的,所以这个时候我们需要定义的一种结构就是,一个节点的孩子是可以动态的增加的࿰…

impala 本年格式化时间_hive,hbase,impala之间的对比
hbase在三者中更注重的是存储,它实现了类似mysql的double write机制,但是它是一种NoSQL的数据库,并且是可以支持列式存储的,算是比较大的一个内存Hash表。hbase也采用了类似mysql中的mvcc的思想通过时间戳来做版本控制。hbase是在…

简单上手的游戏引擎
物理游戏引擎 GameSalad 转载于:https://www.cnblogs.com/sgdkg/archive/2013/06/14/3135882.html

Linux内核中关于定时器Timer的应用
2019独角兽企业重金招聘Python工程师标准>>> 在Touchscreen驱动中 1 声明 Ad7877.c (\linux-2.6.30.4\drivers\input\touchscreen): struct timer_list timer; /* P: lock */ 2 初始化 在函数 static int __devinit ad7877_probe(struct spi_device *spi) 中 执行 …

类加载过程中几个重点执行顺序整理
类的加载过程: 1、 JVM会先去方法区中找有没有相应类的.class存在。如果有,就直接使用;如果没有,则把相关类的.class加载到方法区 2、 在.class加载到方法区时,会分为两部分加载:先加载非静态内容ÿ…

二叉树 2.0 -- 非递归遍历
二叉树递归遍历存在的问题 如果我们的二叉树只有左子树,而且树的高度还很深的时候,这个时候递归调用遍历的时候,栈帧空间开辟的较大,很可能造成栈溢出。但是我们一个程序中,为堆分配的空间要比栈大的多,这…

田忌赛马贪心算法_田忌赛马 贪心算法
算法实验课回顾田忌赛马问题描述:你一定听说过田忌赛马的故事吧?如果3匹马变成n匹(n<100),齐王仍然让他的马按照优到劣的顺序初赛,田忌可以按任意顺序选择他的赛马出赛。赢一局,田忌可以得到200两银子;输…

[转][小结][三种方法]实现WPF不规则窗体
实现WPF不规则窗体的三种常用的方法如下: 1.使用Blend等工具绘制一个不规则xaml,然后作为窗体的背景。这个可以参考xiaowei0705的这篇博文:WPF制作不规则的窗体 。 2.给window的Clip属性赋Path值。这个可以参考DebugLZQ前面的博文:…

VirtualBox虚拟机网络连接设置的四种方式
这里我先给大家大致讲解下VBox的网络配置及应用。 VirtualBox的提供了四种网络接入模式,它们分别是:1、NAT 网络地址转换模式(NAT,Network Address Translation)2、Bridged Adapter 桥接模式3、Internal 内部网络模式4、Host-only Adapter 主机…

redis-deskmanager 连不上 虚拟机 - centos redis
1、没设置redis密码 : https://blog.csdn.net/HUXU981598436/article/details/54668779 2、关闭防火墙 转载于:https://www.cnblogs.com/Jomini/p/9650650.html

数据库1.0 -- 数据库的基本操作
安装数据库 安装数据库的时候我们需要安装三个软件,使用下面的命令,可能还会出现一些问题,关于数据库的安装,大家可以上网自行百度 yum install mysql yum install mysql-server yum install mysql-devel 我个人的理解大概是这…

12,缓冲运动。匀速运动停止条件
缓冲运动:iSpeed(iTarget-oDiv.offsetLeft)/7;速度离目标点越远,速度越大,离目标点越近速度越小; 只支持1px是最小单位,没有0.5px。所以当iSpeed为小数时如(0.7),oDiv.style.LeftoDi…

如何配置mac的mysql环境_mac安装mysql数据库及配置环境变量
安装mysql下载mysql。我下载的是:mysql-8.0.11-macos10.13-x86_64.dmg双击打开mysql-8.0.11-macos10.13-x86_64.dmg,然后双击mysql-8.0.11-macos10.13-x86_64.pkg一路点击继续,傻瓜式安装,没什么好说的此处选择“Use Legacy Passw…

三层交换机vlan间访问(第一种方式)
真的是原创,但是得感谢Ys_routesim软件的制作方。我将命令调试后并进行了解释。若是属于侵权,请立即告知我。不过学习了网工后,大段解读源代码不属于侵权吧。呵呵。 交换机的三层交换实际是具有路由功能的交换机,比如思科的Cisco …

(转载)深入浅出设计模式——桥接模式(Bridge Pattern)
模式动机设想如果要绘制矩形、圆形、椭圆、正方形,我们至少需要4个形状类,但是如果绘制的图形需要具有不同的颜色,如红色、绿色、蓝色等,此时至少有如下两种设计方案: 第一种设计方案是为每一种形状都提供一套各种颜色…

数据库2.0 -- 数据类型和数据表的基本操作
mysql支持多种数据类型,一般可以分为,数值,日期时间和字符(串) 数值类型 日期和时间类型 字符串类型 创建数据表 我们首先应该明白的就是一个结构的问题,一个用户可以管理多个数据库,每个数据…