【c++迭代器模拟实现】

news2024/11/16 12:39:13

目录:

  • 前言
  • 一、STL初始
  • 二、六大组件之迭代器
    • 迭代器初始
    • 迭代器的模拟实现
      • (1)victor
      • 正向迭代器
      • 反向迭代器1
      • 反向迭代器2
      • 反向迭代器3
    • (2)list
      • 正向迭代器
      • 反向迭代器
  • 总结

前言

打怪升级:第52天
在这里插入图片描述

一、STL初始

  1. 什么是STL
    STL(standard template libaray-标准模板库):是C++标准库的重要组成部分,不仅是一个可复用的组件库,而且是一个包罗数据结构与算法的软件框架。
  2. STL的版本
    原始版本
    Alexander Stepanov、Meng Lee 在惠普实验室完成的原始版本,本着开源精神,他们声明允许任何人任意运用、拷贝、修改、传播、商业使用这些代码,无需付费。唯一的条件就是也需要向原始版本一样做开源使用。 HP 版本–所有STL实现版本的始祖。
    P. J. 版本
    由P. J. Plauger开发,继承自HP版本,被Windows Visual C++采用,不能公开或修改,缺陷:可读性比较低,符号命名比较怪异。
    RW版本
    由Rouge Wage公司开发,继承自HP版本,被C+ + Builder 采用,不能公开或修改,可读性一般。
    SGI版本
    由Silicon Graphics Computer Systems,Inc公司开发,继承自HP版 本。被GCC(Linux)采用,可移植性好,可公开、修改甚至贩卖,从命名风格和编程 风格上看,阅读性非常高。
    如果大家了解过侯捷老师写的那本《STL源码剖析》就会知道,这本书参考的原码也是来自 SGI版本 – stl30,
    下方,我也会仿照SGI中的代码风格来模拟实现vector和list容器的迭代器。

STL 提供了六大组件,彼此组合套用协同工作。这六大组件分别是:

这里是引用


二、六大组件之迭代器

迭代器初始

迭代器作为算法与容器之间的沟通枢纽,在stl中有着外交官的作用,由于每一个容器的数据结构不同,所以它们都有自己专属的一套迭代器,在SGI版本中,迭代器就是原生指针或者对原生指针的封装,并且对原生指针的封装也是为了模拟指针的功能,从而统一对容器的访问方式,极大的降低学习使用的成本。
那么下面,就让我们开始操作吧~!


迭代器的模拟实现

(1)victor

vector的底层是数组,因此支持随机访问,vector的正向迭代器可以直接使用原生指针;
vector的反向迭代器需要对原生指针进行封装,让它的行为可以和正向迭代器保持一致。

正向迭代器

示例:

namespace kz  // 放到命名空间中,防止与库里的vector冲突
{
	template<class T>  // 模板
	class vector
	{
	
		 typedef T* iterator;    //  正向迭代器直接使用原生指针 -- typedef是为了保持各个容器的迭代器命名相同
	
	     typedef const T* const_iterator;  //  需要单独有一份const版本 -- 因为常量对象不能使用非常量成员函数
	
	     iterator begin()
	     {
	         return _start;
	     }
	
	     iterator end()
	     {
	         return _finish;
	     }
	
	     const_iterator cbegin() const
	     {
	         return _start;
	     }
	
	     const_iterator cend() const
	     {
	         return _finish;
	     }
	        
	};

}


void Test_iterator1()
{
	kz::vector<int>arr(10);
	for (int i = 0; i < 10; ++i)
		arr[i] = i;

	kz::vector<int>::iterator it = arr.begin();
	// for (; it < arr.end(); ++it)  // vector中迭代器比较可以使用 < ,因为vector底层是数组,是一块连续的空间
	for (; it != arr.end(); ++it)   //  但是其他迭代器如list就不可以,因此为了所有迭代器保持一致,最好使用 != 
		cout << *it << " ";
	cout << endl << endl;
}

在这里插入图片描述


struct A
{
	int _a1;
	int _a2;
};

void Test_iterator2()
{
	kz::vector<A>arr(6);
	for (int i = 0; i < 6; ++i)
		arr[i] = { i, i + 10 };

	kz::vector<A>::iterator it = arr.begin();
	for (; it != arr.end(); ++it) 
		cout << (*it)._a1 << " " << it->_a2 << endl;
}

这里是引用


反向迭代器1

vector的反向迭代器要和正向迭代器保持一致那就需要模拟正向迭代器的全部功能:++,–,比较大小,解引用,下标访问,以及如果数据元素是自定义类型我们还需要提供重载的箭头运算符: ->

    template<class T>
    struct Reverse_iterator
    {
        typedef Reverse_iterator<T> self;
        T* _it;

        Reverse_iterator(T* t = nullptr)
            :_it(t)
        {}

        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;
        }

        bool operator>(const self& t)const
        {
            return _it > t._it;
        }

        bool operator>=(const self& t)const
        {
            return _it > t._it || _it == t._it;
        }

        bool operator<(const self& t)const
        {
            return _it < t._it;
        }

        bool operator<=(const self& t)const
        {
            return _it < t._it || _it == t._it;
        }

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

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

        T& operator[](int n)
        {
            return _it[-n];
        }

        T& operator*()
        {
            return *_it;
        }

        T* operator->()
        {
            return _it;
        }
    };


namespace kz  // 放到命名空间中,防止与库里的vector冲突
{
	template<class T>  // 模板
	class vector
	{
	
		 typedef T* iterator;    //  正向迭代器直接使用原生指针 -- typedef是为了保持各个容器的迭代器命名相同
	
	     typedef const T* const_iterator;  //  需要单独有一份const版本 -- 因为常量对象不能使用非常量成员函数
	     
		 typedef Reverse_iterator<T> reverse_iterator;    
	
	     iterator rbegin()
	     {
	         return _finish-1;
	     }
	
	     iterator rend()
	     {
	         return _start-1;
	     }
	
	     const_iterator crbegin() const
	     {
	         return _finish-1;
	     }
	
	     const_iterator crend() const
	     {
	         return _start-1;
	     }
	        
	};

}


const int CNT = 10;
void Test_iterator3()  
{
	kz::vector<int>arr(CNT);
	for (int i = 0; i < CNT; ++i)
		arr[i] = i;

	kz::vector<int>::reverse_iterator rit = arr.rbegin(); //  反向打印
	while (rit != arr.rend())
	{
		cout << *rit << " ";
		++rit;
	}
	cout << endl << endl;
}

在这里插入图片描述

反向迭代器2

我们现在反向迭代器写好了,但是好像少了些什么:const迭代器好像还没有设计 – 非const对象的元素是可以修改的,而const迭代器不行,所以在对迭代器的解引用方面需要进行限制区分,
那么我们该如何实现呢?

  1. 我们可以和前面写begin(), 和const begin();那样将迭代器再拷贝一份并且改为const,这种做法好像没有问题,嗯~,其实这样确实没有问题,这也是我们这些“普通人”很容易想到的方法,可以解决问题,这里就不做演示;
  2. 不过下面,我们还是要来看一看大佬们是如何设计的,即使那不是我们现在可以达到的思想境界,我们也可以来瞻仰膜拜一番“斗宗强者”的真正实力:
    template<class T, class Ref, class Ptr>
    struct Reverse_iterator
    {
        typedef Reverse_iterator<T, Ref, Ptr> self;
        T* _it;

        Reverse_iterator(T* t = nullptr)
            :_it(t)
        {}

        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;
        }

        bool operator>(const self& t)const
        {
            return _it > t._it;
        }

        bool operator>=(const self& t)const
        {
            return _it > t._it || _it == t._it;
        }

        bool operator<(const self& t)const
        {
            return _it < t._it;
        }

        bool operator<=(const self& t)const
        {
            return _it < t._it || _it == t._it;
        }

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

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

        Ref operator[](int n)
        {
            return _it[-n];
        }

        Ref operator*()
        {
            return *_it;
        }

        Ptr operator->()
        {
            return _it;
        }
    };


namespace kz
{
	template<class T>
	class vector
	{
		
	 typedef Reverse_iterator<T, T&, T*> reverse_iterator;   //  在容器中对反向迭代器的使用
     typedef Reverse_iterator<T, const T&, const T*> const_reverse_iterator;
		reverse_iterator rbegin()
        {
            return reverse_iterator(_finish - 1);
        }

        reverse_iterator rend()
        {
            return reverse_iterator(_start - 1);
        }

        const_reverse_iterator crbegin() const
        {
            return const_reverse_iterator(_finish - 1);
        }

        const_reverse_iterator crend() const
        {
            return const_reverse_iterator( _start - 1);
        }

	};

}

这里是引用

const int CNT = 10;
void Test_iterator3()
{
	kz::vector<int>arr(CNT);
	for (int i = 0; i < CNT; ++i)
		arr[i] = i;

	//kz::vector<int>::const_reverse_iterator rit = arr.crbegin();
	auto crit = arr.crbegin();
	while (crit != arr.crend())
	{
		cout << *crit << " ";
		++crit;
	}
	cout << endl << endl;
}

这里是引用

与第一种方法相比,这样可以减少代码的长度,降低了维护成本,管理更加方便(这里代码少,如果代码有几千上万行效果就会更加明显)。

反向迭代器3

如果仅仅对vector来说,第二种的写法已经是非常优雅了,不过,如果放眼所有的stl容器,每一个拥有反向迭代器的容器我们都需要重写一份反向迭代器的代码,虽然各个代码都不一样,但是还是显得冗余,
我们的大佬们对反向迭代器的实现并不满意,为了化简实现方法,简短代码,大佬们抓耳挠腮、废寝忘食、头悬梁 …(bushi),最终通过对正向迭代器的复用,来实现反向迭代器的功能,当这种方法问世之后,大家惊为天人,纷纷为他们的灵光乍现点赞(家里门槛都被踩塌了啊·)。
那么我们就不废话,请看代码:

		/  reverse
    // 适配器 -- 复用
    template<class Iterator, class Ref, class Ptr>
    struct Reverse_iterator
    {
        typedef Reverse_iterator<Iterator, Ref, Ptr> self;

        Iterator _it; //  _it 是一个正向爹迭代器
        
        Reverse_iterator(Iterator t)
            :_it(t)
        {}


        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*()
        {
            return *(_it - 1);
        }


        Ptr operator->()
        {
            return _it;
        }
        Ref operator[](int i)
        {
            return _it[-i - 1];
        }


        bool operator==(const self& t)
        {
            return t._it == _it;
        }
        bool operator!=(const self& t)
        {
            return t._it != _it;
        }
        // 和list有些不同的是:vector的底层是数组,在内存中为一块连续的空间,因此可以进行>,<的比较
        bool operator>(const self& t) 
        {
            return t._it > _it;
        }
        bool operator>=(const self& t)
        {
            return t._it >= _it;
        }
        bool operator<(const self& t)
        {
            return t._it < _it;
        }
        bool operator<=(const self& t)
        {
            return t._it <= _it;
        }
      

    };


		reverse_iterator rbegin()
		{
			return reverse_iterator(end());
		}

		const_reverse_iterator crbegin() const
		{
			return const_reverse_iterator(end());
		}

		reverse_iterator rend()
		{
			return reverse_iterator(begin());
		}

		const_reverse_iterator crend() const
		{
			return const_reverse_iterator(begin());
		}

至此,反向迭代器的实现就到了到了终点,而上面的之中方法也是我们现在依旧在使用的。


(2)list

list的底层是一个带头双向循环链表,不支持迭代器,当然也不会支持> , < 等比较函数,
下面我们就直接上手操作:

正向迭代器

	template<class T, class Ref, class Ptr>
	struct Reverse_iterator
	{
		typedef Reverse_iterator<T, Ref, Ptr> self;
		typedef list_node<T> node;
		node* _node;         //  T依然是节点指针

		Reverse_iterator(node* n = node())
			:_node(n)
		{}

		self& operator++()  //  ++指向下一个节点
		{
			_node = _node->_prev;

			return *this;
		}

		self operator++(int)
		{
			self tmp(*this);
			_node = _node->_prev;

			return tmp;
		}

		self& operator--()
		{
			_node = _node->_next;

			return *this;
		}

		self operator--(int)
		{
			self tmp(*this);
			_node = _node->_next;

			return tmp;
		}

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

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

		Ref operator*()  //  
		{
			return _node->_val;
		}

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

	};

const int CNT = 10;
void Test_list1()
{
	kz::list<int> l1;
	for (int i = 0; i < CNT; ++i)
		l1.push_back(i);

	kz::list<int>::iterator it = l1.begin();
	while (it != l1.end())
	{
		cout << *it << " ";
		++it;
	}
	cout << endl << endl;
}

这里是引用

反向迭代器

vector中的反向迭代器复用了正向iterator,也就是说只要有正向迭代器的容器都可以使用该反向迭代器模板,
而所有的模板都是拥有正向迭代器的,因此,只要这个容器可以使用反向迭代器那么就都可以使用同一份reverse_iterator模板,
在stl_30(stl的SGI版本中一个)是将反向迭代器单独封装了一个头文件,所有需要的容器只需包头文件即可,不需要再单独写一份反向迭代器的代码。


const int CNT = 10;
void Test_list2()
{
	kz::list<int> l1;
	for (int i = 0; i < CNT; ++i)
		l1.push_back(i);

	kz::list<int>::reverse_iterator it = l1.rbegin();
	while (it != l1.rend())
	{
		cout << *it << " ";
		++it;
	}
	cout << endl << endl;
}

这里是引用


总结

以上就是我们迭代器的讲解以及模拟实现的全部内容,各个容器的正向迭代器各不相同,不过只要实现了正向迭代器,反向迭代器的实现就都可以使用同一个模板来实例化,从这里我们也可以看出我们的前辈们是多么的实力雄厚、深不可测,当我们还在为单个容器的反向迭代器头疼的时候,大佬们的眼光就已经放在了所有容器了。
reverse_iterator的最终实现请查看 上方的《反向迭代器3》;
感谢大家的来访,希望可以给各位带来启发与帮助。

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

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

相关文章

和chatgpt一样的大模型LLaMA可以运行在pc上?

未来已来,大模型依据压缩模型的方式,可以在普通的PC上运行. LLaMA Facebook的LLaMA 模型和Georgi Gerganov 的llama.cpp的结合。 LLaMA&#xff0c;这是一组包含 7B 到 65B 参数的基础语言模型。我们在数万亿个令牌上训练我们的模型&#xff0c;并表明可以仅使用公开可用的数…

【Android入门到项目实战-- 9.1】—— 传感器的使用教程

目录 传感器的定义 三大类型传感器 1、运动传感器 2、环境传感器 3、位置传感器 传感器开发框架 1、SensorManager 2、Sensor 3、SensorEvent 4、SensorEventListener 一、使用传感器开发步骤 1、获取传感器信息 1)、获取传感器管理器 2)、获取设备的传感器对象列…

Java红黑树

概述 红黑树是一种自平衡的二叉查找树&#xff0c;是计算机科学中用到的一种数据结构。1972年出现的&#xff0c;当时被称之为平衡二叉B树。在1978年被修改为红黑树。红黑树是一种特殊的二叉查找树&#xff0c;红黑树上的每一个节点都有存储位表示节点的颜色。每一个节点可以是…

Java枚举:为什么它是单例模式的最佳选择?

前言 单例模式&#xff0c;是工作中比较常见的一种设计模式&#xff0c;通常有两种实现方式&#xff0c;懒汉式和饿汉式。但是这两种实现方式存在一些问题。懒汉式需要在多线程环境下使用同步锁机制来保证只有一个实例被创建&#xff0c;这会影响程序的性能。而饿汉式在类加载时…

《发展心理学——儿童与青少年》读书笔记

这是我读的第一本关于育儿教育类的书&#xff0c;该书的作者是David R. Shaffer&#xff0c;由北京师范大学博士生导师邹泓审校&#xff0c;由其底下的博士生们翻译。我看的是中文第九版。下面是我在阅读此书时做的关键摘录和部分感想&#xff1a; 第1章 导论:发展心理学及其研…

Java基础(二十一):集合源码

Java基础系列文章 Java基础(一)&#xff1a;语言概述 Java基础(二)&#xff1a;原码、反码、补码及进制之间的运算 Java基础(三)&#xff1a;数据类型与进制 Java基础(四)&#xff1a;逻辑运算符和位运算符 Java基础(五)&#xff1a;流程控制语句 Java基础(六)&#xff1…

耗时2.5h含泪打造windows10家庭版docker安装

文章目录 一、事出有因二、安装流程Problem1Problem2Problem3 三、胜利的曙光 一、事出有因 由于最近需要跑通github上的一个代码&#xff0c;那个github上的代码需要通过docker部署到本地&#xff0c;但是我的电脑上并没有docker,真的是含泪历时2.5h才把docker在我的windows电…

【Python成长之路】基于Flask-admin库,结合html+vue,实现前后端数据传递

一、前言 前面已经做了Flask-admin库的基本介绍和几个库常用功能如何使用&#xff0c;若不了解请移步到以下博客&#xff1a; 1、?《【Python成长之路】基于Flask-admin库&#xff0c;编写个人工作平台代码详述》 2、?《【Python成长之路】基于Flask-admin库&#xff0c;编…

DP练习题

1.减操作(ACWING) 若有 a b c d e f g 几个数&#xff0c; 先对位置d操作 变成 a b c d - e f g 再对c操作 变成 a b c - (d-e) f g 仔细分析后得出结论&#xff1a;对于第一个数如a, 它一定为正数&#xff0c;第二个数b,一定为负数&#…

Java并发(四)----线程运行原理

1、线程运行原理 1.1 栈与栈帧   Java Virtual Machine Stacks &#xff08;Java 虚拟机栈 JVM&#xff09; 我们都知道 JVM 中由堆、栈、方法区所组成&#xff0c;其中栈内存是给谁用的呢&#xff1f;其实就是线程&#xff0c;每个线程启动后&#xff0c;虚拟机就会为其分…

java 解密springboot的WEB端口是谁启动的之内嵌tomcat

找到项目的 pom.xml 看到下面的spring-boot-starter-web 我们按住 Ctrl 点击进去 里面就有一个 tomcat 简单说 我们的程序能启动起tomcat端口 就是靠的这个东西 简单说 就是在程序中嵌了一个tomcat服务器 这里 可能就有小伙伴蒙了 不是把程序放在服务器上运行吗&#xff1f…

Linux Driver 和Device匹配过程分析(2)

Linux Driver 和Device匹配过程分析&#xff08;2&#xff09; 1 device注册流程2&#xff0c;driver注册匹配过程&#xff1a;2.1 pci_register_driver2.1.1 nvme_init2.1.2 pci_register_driver2.1.3 __pci_register_driver2.1.4 driver_register2.1.5 bus_add_driver2.1.6 d…

读书笔记——《2001太空漫游》

阿瑟克拉克神作&#xff0c;任何一个科幻迷都绕不开的一部作品。很早就听说过其大名&#xff0c;因为之前看过电影版的&#xff0c;总感觉少了点新鲜感&#xff0c;这本书就一直在书架上没有拿出来看。但是看过这本书后&#xff0c;我可以很负责任的说&#xff0c;全书都充满新…

【递推专题】常见的递推“模型”总结

目录 1.斐波那契数列分析&#xff1a;代码&#xff1a; 2.平面分割问题分析&#xff1a; 3.汉诺塔问题分析&#xff1a; 4.卡特兰数分析&#xff1a; 5.第二类斯特林数总结&#xff1a; 1.斐波那契数列 分析&#xff1a; 斐波那契数列又称兔子数列&#xff0c;其原理来源于兔子…

dangerousRemoteUrlIpcAccess

问题描述&#xff1a; 在使用Tauri窗口加载外部链接时&#xff0c;需要也能继续使用Tauri API与Rust交互。按照官方发布通告中的代码添加配置&#xff1a; "security": {"dangerousRemoteUrlIpcAccess": [ { "windows": ["main", &qu…

在Linux中进行Jenkins部署(maven-3.9.1+jdk8)

Jenkins部署在公网IP为x.x.x.x的服务器上 maven-3.9.1要安装在jdk8环境中 环境准备 第一步&#xff0c;下载server-jre-8u202-linux-x64.tar.gz安装包。 登录地址&#xff1a;https://www.oracle.com/java/technologies/javase/javase8-archive-downloads.html下载server-j…

Maven命令和配置详解

Maven命令和配置详解 1. pom基本结构2. build基本结构3. Maven命令详解3.1 打包命令3.2 常用命令3.3 批量修改版本-父子pom4. Maven配置详解4.1 settings.xml4.2 项目内的maven工程结构Maven POM构建生命周期工程实践1. pom基本结构 <?xml versi

《程序员面试金典(第6版)》面试题 16.13. 平分正方形(直线的斜截式方程,C++)

题目描述 给定两个正方形及一个二维平面。请找出将这两个正方形分割成两半的一条直线。假设正方形顶边和底边与 x 轴平行。 每个正方形的数据square包含3个数值&#xff0c;正方形的左下顶点坐标[X,Y] [square[0],square[1]]&#xff0c;以及正方形的边长square[2]。所求直线穿…

JDK8 中Arrays.sort() 排序方法解读

一、引言 在刷算法的时候经常需要对数组进行排序&#xff0c;第一反应就是直接使用java.util包下的Arrays.sort()方法直接排序。但在刷算法时会通过时间复杂度和空间复杂度对实现的算法进行评价&#xff0c;因此我们需对Arrays.sort()方法有所了解。 本文先行介绍Arrays.sort…

图的基本概念和术语

图&#xff1a;G&#xff08;V,E&#xff09; V:顶点&#xff08;数据元素&#xff09;的又穷非空集合&#xff1b; E:边的有穷集合。 无向图&#xff1a;每条边都是无方向的G2 有向图&#xff1a; 每条边都是有方向的G1 完全图&#xff1a;任意两个点都有一条边相连 假设…