迭代器——STL关键所在

迭代器基本介绍:

STL的设计中心思想在于:将容器和算法分开,彼此独立设计,最后再以一个胶合剂连接在一起。而算法和数据容器的泛型化从技术角度来说并不难实现,而如何将两者联系起来才是问题的关键所在。而迭代器恰恰扮演者这个角色。迭代器定义的位置最好是在容器内,将定义的任务交给了容器的设计者,因为每一种容器都对应一种迭代器,而定义在内部也不会暴露容器的内部元素。

看看下面的find函数:

可以看出find接受两个迭代器和一个目标对象。只要给予不同的迭代器find就可以对不同的容器进行查找了。

迭代器是一种类似指针的对象,而指针的各种行为中最常见也是最重要的便是内容提领和成员访问。因此,迭代器最重要的编程工作就是对operator*和operator->进行重载。

在学习数据结构的时候list一直是一个重点,以前也曾模拟实现过list,如今何不为list设计一个迭代器来将其数据容器与算法粘合起来呢?

其接口及结点实现如下:

 template <class T>
struct __ListNode
{
	__ListNode<T>* _next;
	__ListNode<T>* _prev;
	T _data;

	__ListNode(const T& x = T())
		:_next(NULL)
		, _prev(NULL)
		, _data(x)
	{}
};

template <class T>
class List
{
public:
	typedef __ListNode<T> Listnode;

	List()
	{
		_head = new Listnode;
		_head->_next = _head;
		_head->_prev = _head;
	}

	~List()
	{}
	void Insert(const T& x);

	Iterator Erase(const T& x);

protected:
	Listnode* _head;

};

       模拟实现简化版:

我们自己实现的List也可以套用find函数,只需对其进行一个迭代器的设计就OK了。

具体实现如下:

template<class T,class Ref,class Ptr>
struct __ListIterator
{
	typedef __ListIterator<T, Ref, Ptr> Self;
	typedef T ValueType;
	typedef Ptr Pointer;
	typedef Ref Reference;

	__ListNode<T>* _node;

	__ListIterator(__ListNode<T>* x)
		:_node(x)
	{}
	__ListIterator()
	{}

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

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

	Reference operator*()
	{
		return _node->_data;
	}

	/*Reference operator->()
	{
		return _node->_data;
	}*/
	Self& operator++()			 //前置
	{
		_node = _node->_next;
		return *this;
	}

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

	Self& operator--()
	{
		_node = _node->_prev;
		return *this;
	}

	Self& operator--(int)
	{
		Self tmp(*this);
		_node = _node->_prev;
		return tmp;
	}
};

       迭代器型别和trait编程:

迭代器的型别主要有五种:value_type,catalog,reference,pointer,diffrence。分别代表这迭代器所指对象类型,迭代器类型,迭代器所指类型引用,迭代器所指类型指针,用什么类型表示迭代器之间距离(如int类型)。

这里就不得不说下STL里面为了提取这些迭代器都有的特性用到的一个技法——trait。

STL为所有的迭代器提供一个型别类型,我们每定义一个迭代器都必须继承该型别类型,这样就可以保证符合STL的规范,防止遗漏。

template <class Category, class T, class Distance = ptrdiff_t,  
          class Pointer = T*, class Reference = T&>  
struct iterator {  
  typedef Category  iterator_category;  
  typedef T         value_type;  
  typedef Distance  difference_type;  
  typedef Pointer   pointer;  
  typedef Reference reference;  
};

当定义迭代器的时候,必须给定迭代器的特性。STL为提取迭代器的特性,提供了一个模板类iterator_trait,适用于所有的迭代器和原生指针。

指针并非类型,因此需要偏特化成一个模板对应指针的特性,可以看出,指针是随机访问迭代器类型。

迭代器的类型有五种:输入、输出、前向、双向、随机访问五种迭代器,输入和输出分别只读和只写,只能向前不能向后,前向迭代器可以进行读写,只能向前,双向迭代器可以向前和向后移动,但每次只能移动一次,随机访问迭代器可以跳跃移动,与原生指针操作相同。

STL中构建了这五种类别,用于标识迭代器的类别。

struct input_iterator_tag {};  
struct output_iterator_tag {};  
struct forward_iterator_tag : public input_iterator_tag {};  
struct bidirectional_iterator_tag : public forward_iterator_tag {};  
struct random_access_iterator_tag : public bidirectional_iterator_tag {};

可以看出继承关系,使用template元编程技术,之所以使用结构体或类,是为了进行参数推导,将判断在编译期执行而非运行期,因为每个迭代器操作不同,因此需要不同的函数版本对应不同迭代器。

以上讲的是迭代器的特性提取,还有类型的特性提取。类型的型别主要有五种:has_trivial_default_constructor、has_trivial_copy_constructor、has_trivial_assignment_operator、has_trivial_destructor、is_POD_type。

        迭代器失效:

迭代器就不得不提到一个问题,迭代器失效。

以vector为例,当我们插入一个元素时它的预分配空间不够时,它会重新申请一段新空间,将原空间上的元素复制到新的空间上去,然后再把新加入的元素放到新空间的尾部,以满足vector元素要求连续存储的目的。而后原空间会被系统撤销或征做他用,于是指向原空间的迭代器就成了类似于“悬垂指针”一样的东西,指向了一片非法区域。如果使用了这样的迭代器会导致严重的运行时错误就变得很自然了。这就是由迭代器失效引起的。当然删除一个元素时也可能引起迭代器失效。erase操作是在原空间上进行的,假设有一个存有"12345"序列的vector<int>容器原本指向3的迭代器在我删除2之后就变成指向4了。

说了这么多似乎可以归纳一下迭代器失效的类型了:
      1.由于容器元素整体“迁移”导致存放原容器元素的空间不再有效,从而使得指向原空间的迭代器失效。

2.由于删除元素使得某些元素次序发生变化使得原本指向某元素的迭代器不再指向希望指向的元素。

时间: 2024-10-12 09:23:50

迭代器——STL关键所在的相关文章

STL适配器(adapters)

定义:将一个class的接口转换为另一个class的接口,使原本因接口不兼容而不能合作的classes,可以一起运作.适配器扮演者轴承.转换器的角色. 分类: 1.容器适配器:改变容器接口. STL提供两个容器迭代器:queue和stack.它们都是修饰deque后成为另一种风貌的容器. 2.迭代器适配器:改变迭代器接口. Insert Iterator:将容器绑定到back_insert_iterator.front_insert_iterator.insert_iterator.它们都是一个

C++ STL vector容器学习

STL(Standard Template Library)标准模板库是C++最重要的组成部分,它提供了一组表示容器.迭代器.函数对象和算法的模板.其中容器是存储类型相同的数据的结构(如vector,list, deque, set, map等),算法完成特定任务,迭代器用来遍历容器对象,扮演容器和算法之间的胶合剂. 模板类vector 在计算中,矢量(vector)对应数组,它的数据安排以及操作方式,与array非常类似.在C++中,使用vector模板类时,需要头文件包含#include<v

容器适配器、STL算法简介

可以用某种顺序容器来实现 (让已有的顺序容器以栈/队列的方式工作) 1) stack: 头文件 <stack> 栈 -- 后进先出 2) queue: 头文件 <queue> 队列 -- 先进先出 3) priority_queue: 头文件 <queue> 优先级队列 -- 最高优先级元素总是第一个出列 都有3个成员函数: push: 添加一个元素; top: 返回栈顶部或队头元素的引用 pop: 删除一个元素 容器适配器上没有迭代器 STL中各种排序, 查找, 变序

标准模板库--STL

标准模板库STL 1.泛型程序设计 C++ 语言的核心优势之一就是便于软件的重用 C++中有两个方面体现重用: 1.面向对象的思想:继承和多态,标准类库 2.泛型程序设计(generic programming) 的思想: 模板机制,以及标准模板库STL 简单地说就是使用模板的程序设计法. 将一些常用的数据结构(比如链表,数组,二叉树)和算法(比如排序,查找)写成模板,以后则不论数据结构里放的是什么对象,算法针对什么样的对象,则都不必重新实现数据结构,重新编写算法.标准模板库 (Standard

初探STL之容器适配器

容器适配器 特点 用某种顺序容器来实现(让已有的顺序容器以栈/队列的方式工作) 分类 1) stack: 头文件 <stack> ? 栈 -- 后进先出 2) queue: 头文件 <queue> ? 队列 -- 先进先出 3) priority_queue: 头文件 <queue> ? 优先级队列 -- 最高优先级元素总是第一个出列 注: 容器适配器上没有迭代器 STL中各种排序, 查找, 变序等算法都不适合容器适配器 Stack 特点 1.stack 是后进先出的数

[技术] OIer的STL入门教程

注: 本文主要摘取STL在OI中的常用技巧应用, 所以可能会重点说明容器部分和算法部分, 且不会讨论所有支持的函数/操作并主要讨论 C++11 前支持的特性. 如果需要详细完整的介绍请自行查阅标准文档. 原始资料源于各大C++参考信息网站/C++标准文档和Wikipedia. 1.概述 首先, 什么是STL? STL, 即标准模板库, 全称Standard Template Library , 主要包含4个组件, 即算法, 函数, 容器, 迭代器. 这里的函数似乎主要指函数式编程(FP)中的函数

初步STL该容器适配器

容器适配器 特点 容器一定的顺序来实现(让现有的以集装箱堆放/式工作) 分类 1) stack: 头文件 <stack> ? 栈 -- 后进先出 2) queue: 头文件 <queue> ? 队列 -- 先进先出 3) priority_queue: 头文件 <queue> ? 优先级队列 -- 最高优先级元素总是第一个出列 注: 容器适配器上没有迭代器 STL中各种排序, 查找, 变序等算法都不适合容器适配器 Stack 特点 1.stack 是后进先出的数据结构

C++--第23课 - STL简介

第23课 - STL简介 1. 标准模板SLT C++的作用就是提高程序书写的效率,那么就得代码复用. STL,即:Standard Template(样板) Library,是C++的一部分(常用的数据结构).STL是常用数据结构和算法的集合.STL的目标是标准化组件,提高开发效率和程序可靠性. STL库作为为C++的一部分与编译器一同被发布.STL主要由以下3个部分组成: 容器(Container):管理数据的集合 算法(Algorithm):处理集合内的元素 迭代器(Iterator):遍

C++--学习笔记013

攀登c++高峰 第三篇 第8章 STL 标准模板库 算法+容器+迭代器=STL 算法:algorithm 容器:container 迭代器:iterator 容器适配器:container adaptor 函数对象:functor STL容器提供了这样的方便,它允许重复利用已有的实现,从而构造自己的特定类型下的数据结构. STL包含很多计算机科学上的通用算法. 算法通过迭代器来定位和操控容器中的元素.如果没有迭代器,算法和容器就无法相互作用. STL被组织在多个头文件和名字空间中,药想在程序中更