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

这篇具有很好参考价值的文章主要介绍了【c++迭代器模拟实现】。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前言

打怪升级:第52天
【c++迭代器模拟实现】

一、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 提供了六大组件,彼此组合套用协同工作。这六大组件分别是:

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


二、六大组件之迭代器

迭代器初始

迭代器作为算法与容器之间的沟通枢纽,在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;
}

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


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

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


反向迭代器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;
}

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

反向迭代器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);
        }

	};

}

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

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

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

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

反向迭代器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;
}

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

反向迭代器

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

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


总结

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

到了这里,关于【c++迭代器模拟实现】的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • C++从零开始的打怪升级之路(day11)

    这是关于一个普通双非本科大一学生的C++的学习记录贴 在此前,我学了一点点C语言还有简单的数据结构,如果有小伙伴想和我一起学习的,可以私信我交流分享学习资料 那么开启正题 为了巩固前面的知识,最近更新刷题贴,C++进度暂缓 反转字符串中的单词 III  由于还没学

    2024年01月18日
    浏览(39)
  • C++从零开始的打怪升级之路(day12)

    这是关于一个普通双非本科大一学生的C++的学习记录贴 在此前,我学了一点点C语言还有简单的数据结构,如果有小伙伴想和我一起学习的,可以私信我交流分享学习资料 那么开启正题 今天学习了关于模板的知识,下面展开分析 首先我们思考一个问题,如何是实现一个通用

    2024年01月22日
    浏览(42)
  • C++从零开始的打怪升级之路(day20)

    这是关于一个普通双非本科大一学生的C++的学习记录贴 在此前,我学了一点点C语言还有简单的数据结构,如果有小伙伴想和我一起学习的,可以私信我交流分享学习资料 那么开启正题 今天分享的是关于vector的题目 260. 只出现一次的数字 III 给你一个整数数组  nums ,其中恰

    2024年01月25日
    浏览(42)
  • C++从零开始的打怪升级之路(day13)

    这是关于一个普通双非本科大一学生的C++的学习记录贴 在此前,我学了一点点C语言还有简单的数据结构,如果有小伙伴想和我一起学习的,可以私信我交流分享学习资料 那么开启正题 今天学了一些基础的string的函数,刷了一些题,等string学完了再总结语法,函数 把字符串

    2024年01月20日
    浏览(33)
  • C++从零开始的打怪升级之路(day19)

    这是关于一个普通双非本科大一学生的C++的学习记录贴 在此前,我学了一点点C语言还有简单的数据结构,如果有小伙伴想和我一起学习的,可以私信我交流分享学习资料 那么开启正题 今天分享的是关于vector的题目 137. 只出现一次的数字 II 给你一个整数数组  nums  ,除某个

    2024年01月25日
    浏览(47)
  • C++:模拟实现list及迭代器类模板优化方法

    本篇模拟实现简单的list和一些其他注意的点 如下所示是利用拷贝构造将一个链表中的数据挪动到另外一个链表中,构造两个相同的链表 lt作为形参,传引用可以提高传参的效率,同时应该加上const修饰来保证不会因为不小心修改了形参导致外部的实参也被修改,这样的结果是

    2024年02月13日
    浏览(40)
  • [C++历练之路]优先级队列||反向迭代器的模拟实现

    W...Y的主页 😊  代码仓库分享💕 🍔前言: 在C++的宇宙中,优先队列似乎是一座巨大的宝库,藏匿着算法的珍宝。而就在这片代码的天空下,我们不仅可以探索优先队列的神奇,还能够揭开反向迭代器的神秘面纱。让我们一同踏入这个编程的探险之旅,在这里,我们将用C

    2024年02月04日
    浏览(51)
  • 【C++】反向迭代器的模拟实现通用(可运用于vector,string,list等模拟容器)

    🌏博客主页: 主页 🔖系列专栏: C++ ❤️感谢大家点赞👍收藏⭐评论✍️ 😍期待与大家一起进步! 我们要写出一个通用的反向迭代器模拟而且在保证代码简介不繁琐的的情况下,一定程度上使用我们自己模拟的已经封装好的iterator迭代器可以简化许多步骤,首先我们要知

    2024年02月14日
    浏览(56)
  • C++:关于模拟实现vector和list中迭代器模块的理解

    本篇是关于 vector 和 list 的模拟实现中,关于迭代器模块的更进一步理解,以及在前文的基础上增加对于反向迭代器的实现和库函数的对比等 本篇是写于前面模拟实现的一段时间后,重新回头看迭代器的实现,尤其是在模板角度对 list 中迭代器封装的部分进行解析,希望可以

    2024年02月07日
    浏览(45)
  • 深入篇【C++】手搓模拟实现list类(详细剖析底层实现原理)&&模拟实现正反向迭代器【容器适配器模式】

    1.一个模板参数 在模拟实现list之前,我们要理解list中的迭代器是如何实现的。 在vector中迭代器可以看成一个指针,指向vector中的数据。它的解引用会访问到具体的数据本身,++会移动到下一个数据位置上去,这些都是因为vector具有天生的优势:空间上是连续的数组,这样指

    2024年02月15日
    浏览(46)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包