[C ++] —— Implementación de iterador de objeto de cadena y contenedor de vectores

Primero, la implementación del iterador del objeto string string

1. Principios básicos

Cuando asignamos una cadena a un objeto, por ejemplo:
String str1 = "hello world",
cuando queremos atravesar la cadena, ¿alguna vez has pensado en cómo atravesar? Debido a que pone un conjunto de caracteres de tipo char en la parte inferior, no podemos verlo.

A partir de esto, presentamos el concepto de tipos de iterador de contenedor.
La implementación específica es la siguiente:

String::iterator it = str1.begin();
	for (; it != str1.end(); ++it)
	{
		cout << *it << " ";
	}
	cout << endl;

El principio es el siguiente:
Inserte la descripción de la imagen aquí
Características:

  1. Este tipo es una clase anidada de tipo contenedor
  2. Los elementos dentro del contenedor no son visibles, y el iterador puede acceder de forma transparente a los valores de los elementos dentro del contenedor. ** Hablando popularmente, no importa qué estructura de datos esté en la parte inferior del contenedor, cómo promoverla. Por ejemplo: los parámetros del algoritmo genérico reciben iteradores, es una función global para todas las funciones,
    hay un conjunto de formas de recorrer todos los elementos del contenedor de manera uniforme
  3. Su función es proporcionar una forma unificada de atravesar el contenedor de forma transparente.

Por lo tanto, necesitamos implementar los operadores relevantes en el iterador nosotros mismos.

2. Implementación del operador.

(1) Proporcione una implementación de iterador para el tipo de cadena
Debido a que es un tipo incrustado, se escribe en el público de la clase de cadena.

Los iteradores también deben proporcionar * sobrecarga del operador para acceder a los valores de los elementos iterados por el iterador, y el iterador desreferencia el acceso a los datos subyacentes del contenedor.

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

(2) El método begin ()
devuelve una representación del iterador que hace su primer elemento subyacente

iterator begin() { return iterator(_pstr); }

(3) El método end ()
devuelve una representación del iterador en la posición posterior del elemento final del contenedor

iterator end() { return iterator(_pstr + length()); }

Segundo, implemente el iterador del contenedor de vectores.

Debido a que vector también es un contenedor, aún podemos usar el iterador anterior,
(1) La implementación específica es la siguiente:

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

(2) Tres formas de recorrido del iterador

Método 1: dado que el vector es un contenedor de matriz, se puede acceder al espacio continuo utilizando subíndices, pero este método de recorrido solo es útil para el vector

int size = vec.size();
	for (int i = 0; i < size; ++i)
	{
		cout << vec[i] << " ";
	}
	cout << endl;

Método 2: método de acceso habitual del iterador

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

Método tres: un método foreach más conveniente en c ++ 11 para atravesar los valores de los elementos internos del contenedor. La capa inferior todavía se atraviesa a través del iterador

for (int val : vec)
	{
		cout << val << " ";
	}
	cout << endl;
Publicado 98 artículos originales · ganado elogios 9 · vistas 3668

Supongo que te gusta

Origin blog.csdn.net/qq_43412060/article/details/105136908
Recomendado
Clasificación