一、string字符串对象的迭代器iterator实现

我们先来看这个例子:使用库中的string,那么string的对象str1叫容器吗?

string str1 = "hello world!";//str1叫容器吗?

叫容器,其底层放了一组char类型的字符,也是容器。
若想用指针遍历其底层字符串数组,我们并不清楚它的数组名,那如何指向它的数组名?此时就需要我们的容器的迭代器了。
迭代器:要访问顺序容器和关联容器中的元素,需要通过"迭代器(iterator)"进行,提供一种统一的方式,来透明的遍历容器。迭代器是一个变量,相当于容器和操纵容器的算法之间的中介。迭代器可以指向容器中的某个元素,通过迭代器就可以读写它指向的元素。从这一点上看,迭代器和指针类似。

不同容器底层数据结构不一样,每一种容器都有自己的迭代器,迭代器按照定义方式分成以下四种:
1.正向迭代器,定义方法如下:

容器类名::iterator  迭代器名;

2.常量正向迭代器,定义方法如下:

容器类名::const_iterator  迭代器名;

3. 反向迭代器,定义方法如下:

容器类名::reverse_iterator  迭代器名;

4.常量反向迭代器,定义方法如下:

容器类名::const_reverse_iterator  迭代器名;

我们通过迭代器可以解决刚才的问题:

string str1 = "hello world!";//str1叫容器吗?
//iterator即容器的迭代器
string::iterator it = str1.begin();
for (; it!=str1.end(); ++it)
{
	cout << *it << " ";
}
cout << endl;

那么这段代码什么意思:
在这里插入图片描述
1.str1对象中存入了各种各样的字符,字符串类型底层的成员变量为私有的,我们无法看见。
2.容器有一个begin()方法,begin()返回它底层的迭代器的表示,it迭代器指向容器的首元素。
3.容器中还有一个end()方法,end()表示容器中最后一个元素的后继位置,循环中it!=end(),++it,将其遍历一遍。
4.底层无论是数组还是链表什么的,如何从当前元素遍历到下一个元素,我们不需要操心;容器底层元素真真正正从一个元素跑到下一个元素,不同数据结构,不同差异都封装在迭代器++运算符重载函数中。
5.迭代器还需要提供 * 运算符重载,访问迭代器所迭代元素的值,迭代器解引用访问的就是容器底层数据。
实现迭代器iterator:

class String
{
public:
	String(const char *p = nullptr)
	{
		if (p != nullptr)
		{
			_pstr = new char[strlen(p) + 1];
			strcpy(_pstr, p);
		}
		else
		{
			_pstr = new char[1];
			*_pstr = '0';
		}
	}
	~String()
	{
		delete[]_pstr;
		_pstr = nullptr;
	}
	String(const String &str)
	{
		_pstr = new char[strlen(str._pstr) + 1];
		strcpy(_pstr, str._pstr);
	}
	String& operator=(const String &str)
	{
		if (this == &str)
		{
			return *this;
		}
		delete[]_pstr;

		_pstr = new char[strlen(_pstr) + 1];
		strcpy(_pstr, str._pstr);
		return *this;
	}
	bool operator>(const String &str)const
	{
		return strcmp(_pstr, str._pstr) > 0;
	}
	bool operator<(const String &str)const
	{
		return strcmp(_pstr, str._pstr) < 0;
	}
	bool operator==(const String &str)const
	{
		return strcmp(_pstr, str._pstr) == 0;
	}
	int length()const
	{
		return strlen(_pstr);
	}
	char& operator[](int index)
	{
		return _pstr[index];
	}
	const char& operator[](int index)const
	{
		return _pstr[index];
	}
	const char* c_str()const//返回字符串底层管理的char*,返回为const char*
	{
		return _pstr;
	}
	//给String字符串类型提供迭代器iterator的实现
	class iterator
	{
	public:
		iterator(char *p = nullptr):_p(p){}
		bool operator!=(const iterator &it)
		{
			return _p != it._p;
		}
		void operator++()
		{
			++_p;
		}
		char&  operator*()
		{
			return *_p;
		}
	private:
		char *_p;
	};
	iterator begin()//返回容器底层首元素迭代器的表示
	{
		return iterator(_pstr);
	}
	iterator end()//返沪容器末尾元素后继位置的迭代器的表示
	{
		return iterator(_pstr + length());
	}
private:
	char *_pstr;
	friend ostream& operator<<(ostream &out, const String &str);
	friend String operator+(const String &lhs, const String &rhs);
};

String operator+(const String &lhs, const String &rhs)
{
	String tmp;
	tmp._pstr = new char[strlen(lhs._pstr) + strlen(rhs._pstr) + 1];
	strcpy(tmp._pstr, lhs._pstr);
	strcat(tmp._pstr, rhs._pstr);
	
	return tmp;
}

ostream& operator<<(ostream &out, const String &str)
{
	out << str._pstr;
	return out;
}

int main()
{
	String str1 = "hello world!";//str1叫容器吗?
	//iterator即容器的迭代器
	String::iterator it = str1.begin();
	//auto it = str1.begin();//自动推导类型
	for (; it!=str1.end(); ++it)
	{
		cout << *it << " ";
	}
	cout << endl;
}

执行结果:成功实现迭代器的++
在这里插入图片描述
也可以使用替换这句:

//String::iterator it = str1.begin();
auto it = str1.begin();//自动推导类型

       auto不是一个类型的“声明”,而是一个“占位符”,编译器在编译期会将auto替换为变量实际的类型。 使用auto定义变量时必须对其进行初始化,在编译阶段编译器需要根据初始化表达式来推导auto的实际类型。它自动推导变量类型是根据“=”右侧的变量类型决定的。

我们还可以利用C++11标准中的foreach方式来遍历容器内部元素的值:其底层还是通过迭代器进行遍历的。

//C++11 foreach方式来遍历容器内部元素的值
for (char ch : str1)
{
	cout << ch << " ";
}
cout << endl;

来看一下:执行成功
在这里插入图片描述

二、实现vector容器的迭代器

泛型算法:给所有容器都可以使用,参数接受的都是容器的迭代器。

我们来实现一下vector容器的迭代器:

//容器的空间配置器
template <typename T>
struct Allocator
{
	T* allocate(size_t size)//只负责内存开辟
	{
		return (T*)malloc(sizeof(T) * size);
	}
	void deallocate(void *p)//只负责内存释放
	{
		free(p);
	}
	void construct(T *p, const T &val)//已经开辟好的内存上,负责对象构造
	{
		new (p) T(val);//定位new,指定内存上构造val,T(val)拷贝构造
	}
	void destroy(T *p)//只负责对象析构
	{
		p->~T();//~T()代表了T类型的析构函数
	}
};

template <typename T, typename Alloc = Allocator<T>>
class vector//向量容器
{
public:
	vector(int size = 10)//构造
	{
		//_first = new T[size];
		_first = _allocator.allocate(size);
		_last = _first;
		_end = _first + size;
	}
	~vector()//析构
	{
		//delete[]_first;
		for (T *p=_first; p!=_last; ++p)
		{
			_allocator.destroy(p);//把_first指针指向的数组的有效元素析构
		}
		_allocator.deallocate(_first);//释放堆上的数组内存
		_first = _last = _end = nullptr;
	}
	vector(const vector<T> &rhs)//拷贝构造
	{
		int size = rhs._end - rhs._first;//空间大小
		//_first = new T[size];
		_first = _allocator.allocate(size);
		int len = rhs._last - rhs._first;//有效元素
		for (int i=0; i<len; ++i)
		{
			//_first[i] = rhs._first[i];
			_allocator.construct(_first+i, rhs._first[i]);
		}
		_last = _first + len;
		_end = _first + size;
	}
	vector<T>& operator=(const vector<T> &rhs)//赋值运算符重载
	{
		if (this == &rhs)
		{
			return *this;
		}

		//delete[]_first;
		for (T *p=_first; p!=_last; ++p)
		{
			_allocator.destory(p);//把_first指针指向的数组的有效元素析构
		}
		_allocator.deallocate(_first);//释放堆上的数组内存

		int size = rhs._end - rhs._first;//空间大小
		_first = _allocator.allocate(size);
		int len = rhs._last - rhs._first;//有效元素
		for (int i=0; i<len; ++i)
		{
			_allocator.construct(_first+i, rhs._first[i]);
		}
		_last = _first + len;
		_end = _first + size;
		return *this;
	}
	void push_back(const T &val)//尾插
	{
		if (full())
		{
			expand();
		}
		//*_last++ = val;
		_allocator.construct(_last, val);//_last指针指向的内存构造一个值为val的对象
		_last++;
	}
	void pop_back()//尾删
	{
		if (empty()) return;
		//--_last;
		//不仅要把_last指针--,还需要析构删除的元素
		--_last;
		_allocator.destroy(_last);
	}
	T back()const//返回容器末尾元素值
	{
		return *(_last - 1);
	}
	bool full()const
	{
		return _last == _end;
	}
	bool empty()const
	{
		return _first == _last;
	}
	int size()const//返回容器中元素个数
	{
		return _last - _first;
	}
	T& operator[](int index)
	{
		if (index < 0 || index >= size())
		{
			throw "OutOfRangeException";
		}
		return _first[index];
	}
	//迭代器一般实现成容器的嵌套类型
	class iterator
	{
	public:
		iterator(T *ptr = nullptr)
			:_ptr(ptr){}
		bool operator!=(const iterator &it)const
		{
			return _ptr != it._ptr;
		}
		void operator++()
		{
			_ptr++;
		}
		T& operator*()
		{
			return *_ptr;
		}
		const T& operator*()const
		{
			return *_ptr;
		}
	private:
		T *_ptr;
	};
	iterator begin()
	{
		return iterator(_first);
	}
	iterator end()
	{
		return iterator(_last);
	}
private:
	T *_first;//起始数组位置
	T *_last;//指向最后一个有效元素后继位置
	T *_end;//指向数组空间的后继位置
	Alloc _allocator;//定义容器的空间配置器对象

	void expand()//扩容
	{
		int size = _end - _first;
		//T *ptmp = new T[2*size];
		T *ptmp = _allocator.allocate(2*size);
		for (int i=0; i<size; ++i)
		{
			_allocator.construct(ptmp+i, _first[i]);
			//ptmp[i] = _first[i];
		}
		//delete[]_first;
		for (T *p=_first; p!=_last; ++p)
		{
			_allocator.destroy(p);
		}
		_allocator.deallocate(_first);
		_first = ptmp;
		_last = _first + size;
		_end = _first + 2*size;
	}
};

int main()
{
	vector<int> vec;
	for (int i=0; i<20; ++i)
	{
		vec.push_back(rand()%100);
	}

	int size = vec.size();//[]重载针对vector有意义
	for (int i=0; i<size; ++i)
	{
		cout << vec[i] << " ";//底层是数组,O(1)
	}
	cout << endl;

	vector<int>::iterator it = vec.begin();
	//auto it = vec.begin();
	for (; it!=vec.end(); ++it)
	{
		cout << *it << " ";
	}
	cout << endl;

	for(int val : vec)//底层还是通过容器的迭代器来实现遍历的
	{
		cout << val << " ";
	}
	cout << endl;

	return 0;
}

执行结果:测试成功。
在这里插入图片描述

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐