【C++】STL迭代器

作者前言:学习迭代器之前,我们要先清楚,什么是迭代器?迭代器是干什么用的?为什么要使用迭代器?

迭代器是一个支持指针类型抽象的类对象。提供了一种一般化的方法,对顺序或关联容器类型中的每个元素进行连续访问。

简单来说,迭代器就是用来遍历容器的工具,并可以对容器进行一定的操作。

划重点!!!

迭代器和指针有什么区别?

迭代器不是指针,是类模板,表现的像指针。他只是模拟了指针的一些功能,通过重载了指针的一些操作符 *、++、--、&等。迭代器封装了指针,是一个可遍历STL容器内全部或部分元素的对象,本质是封装了原生指针,是指针概念的一种提升,提供了比指针更高级的行为,相当于一种智能指针。

迭代器返回的是对象引用而不是对象的值,所以const只能输出迭代器使用*取值后的值而不能直接输出其自身。

       或许会有人说:我不用迭代器也一样可以遍历容器,为什么还要用迭代器

       我们现在常用的容器vector,list,map,set,multiset,multimp,deque这几个容器的内部实现由顺序表,链表,红黑树。所以我们要遍历这些容器就要先知道它的底层结构,这样就很麻烦。而iterator就是在底层定义好,任何容器都是同样的遍历方式,这样就算我们不知道底层实现也可以操作容器。

所以Iterator的使用可以让我们在不知道对象内部表示的情况下,按照一定顺序(由iterator提供的方法)访问聚合对象中的各个元素。

迭代器的作用:能够让迭代器与算法不干扰的相互发展,最后又能无间隙的粘合起来,重载了*,++,==,!=,=运算符。用以操作复杂的数据结构,容器提供迭代器,算法使用迭代器;常见的一些迭代器类型有iterator、const_iterator、reverse_iterator和const_reverse_iterator。

举例讲解迭代器:

假设iter为任意容器类型的一个iterator,则:

   ++iterator;//向前移动迭代器,使其指向容器的下一个元素

   *iterator;//返回iterator指向元素的值

每种容器类型都提供一个begin()和一个end()成员元素。

   begin()返回一个iterator,它指向容器的第一个元素

   end()返回一个iterator,它指向容器的末元素的下一个位置

由于模板和嵌套类语法的原因 iterator 的定义看起来有点吓人,例如下面是一对 iterator的定义 它们指向一个内含 string 元素的 vector:

// vector<string> vec;

vector<string>::iterator iter = vec.begin();

vector<string>::iterator iter_end = vec.end();

为了把每个 string 元素打印到标准输出上 我们可以这样写

for( ; iter != iter_end; ++iter )

cout << *iter << '\n'; 

当然,这里 *iter 的运算结果就是实际的 string 对象。

除了iterator类型,每个容器还定义了一个 const iterator类型,对于遍历const容器是必需的,const iterator 允许以只读方式访问容器的底层元素。分析下面给出的一个例子

// 无法通过编译

template < typename type >

int count( const vector< type > &vec, type value )

int count = 0; 

vector< type >::iterator iter = vec.begin(); 

while ( iter != vec.end() )

{  

if ( *iter == value )   

++count;

++iter;

return count;

}

    为什么不能编译呢?思考一下

    问题在于vec是一个const引用,但是我们试图把一个非 const 的iterator绑定在它上面。如果允许这样做,那就没有什么能阻止我们在后面通过iterator修改这个vector的值了。

为了防止这种情况的出现,C++语言要求绑定在onst vector上的iterator也必须是const iterator。我们这样做

// ok: 这次可以通过编译了

vector< type >::const_iterator iter = vec.begin();     

const容器只能被绑定在 const iterator 上 这样的要求与 const 指针只能指向 const 数组的 行为一样 在两种情况下 C++语言都努力保证 const 容器的内容不会被改变。

下面我自己模拟一个vector容器,然后实现一个迭代器来操作这个容器

template<typename T>
class Vector;
template<typename T>
class Iterator
{
public:
	Iterator(Vector<T>* pv, int idx) :
		pvec(pv), index(idx)
	{}
	bool operator!=(const Iterator left)
	{
		return index != left.index;
	}
	T& operator*()const;
	const Iterator operator++(int)
	{
		const Iterator tmp(*this);
		index++;
		return tmp;
	}
	const Iterator operator--(int)
	{
		const Iterator tmp(*this);
		index--;
		return tmp;
	}
	const Iterator operator-(int left)
	{
		return Iterator(pvec, index - left);
	}
private:
	Vector<T>* pvec;
	int index;
};
template<typename T>
class Vector
{
public:
	typedef Iterator<T> iterator;
	Vector()
	{
		parr = new T[2]();
		cursize = 0;
		totalsize = 2;
	}
	iterator begin()
	{
		return iterator(this, 0);
	}
	iterator end()
	{
		return iterator(this, cursize);
	}
	void push_back(T data)
	{
		insert(end(), data);
	}
	void insert(iterator _where, T data)
	{
		if (full())
		{
			resize();
		}
		for (iterator it = end(); it != _where; it--)
		{
			*it = *(it - 1);
		}
		*_where = data;
		cursize++;
	}
	~Vector()
	{
		delete[] parr;
		parr = NULL;
	}
	void resize()
	{
		T* pnewspace = new T[totalsize * 2];
		memcpy(pnewspace, parr, sizeof(T)*totalsize);
		delete[] parr;
		parr = pnewspace;
		totalsize *= 2;
	}
	
	//arr[0] = 10;
	T& operator[](int index)
	{
		return parr[index];
	}
private:
	bool full()
	{
		return cursize == totalsize;
	}
	T* parr;
	int cursize;//当前元素个数  
	int totalsize;//总大小  
};
template<typename T>
T& Iterator<T>::operator*()const
{
	return (*pvec)[index]; //vec[0];
}

测试一下.......

#include<vector>
int main()
{
	Vector<int> vec;
	for (int i = 0; i < 10; i++)
	{
		vec.push_back(i + 1);
	}
	Vector<int>::iterator it = vec.begin();
	while (it != vec.end())
	{
		std::cout << *it << " ";
		it++;
	}
	std::cout << std::endl;
	return 0;
}

 运行结果!!

最后说一下STL迭代器删除元素的问题

这个主要考察的是迭代器失效的问题。

1.对于序列容器vector,deque来说,使用erase(iterator)后,后边的每个元素的迭代器都会失效,但是后边每个元素都会往前移动一个位置,但是erase会返回下一个有效的迭代器。

2.对于关联容器map,set来说,使用了erase(iterator)后,当前元素的迭代器失效,但是其结构是红黑树,删除当前元素不会影响到下一个元素的迭代器,所以在调用erase之前,记录下一个元素的迭代器即可。

3.对于list来说,它使用了不连续分配的内存,并且它的erase方法也会返回下一个有效的iterator,因此上面两种正确的方法都可以使用。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章