跳至主要內容

string迭代器的实现

张威大约 4 分钟c/c++运算符重载迭代器

string迭代器的实现

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

叫容器,其底层放了一组的字符,也是容器。 若想用指针遍历其底层字符串数组,我们并不清楚它的数组名,那如何指向它的数组名?此时就需要我们的容器的迭代器了。

。迭代器是一个变量,相当于容器和操纵容器的算法之间的中介。迭代器可以指向容器中的某个元素,通过迭代器就可以读写它指向的元素。从这一点上看,迭代器和指针类似。

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

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

auto

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

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

foreach

C++11标准中的foreach方式来遍历容器内部元素的值:其底层还是通过进行遍历的

  • 如果自定义类型没有迭代器的实现,则不能用foreach方法遍历
//C++11 foreach方式来遍历容器内部元素的值
for (char ch : str1)
{
	cout << ch << " ";
}
cout << endl;