[C ++] —— Iterator implementation of string object and vector container

First, iterator implementation of string string object

1. Basic principles

When we assign a string to an object, for example:
String str1 = "hello world";
when we want to traverse the string, have you ever thought about how to traverse? Because it puts a set of char type characters at the bottom, we can't see it.

From this, we introduce the concept of container iterator types.
The specific implementation is as follows:

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

The principle is as follows:
Insert picture description here
Features:

  1. This type is a nested class of container type
  2. The elements inside the container are not visible, and the iterator can transparently access the values ​​of the elements inside the container. ** Popularly speaking, no matter what data structure is at the bottom of the container, how to promote it. For example: Generic algorithm parameters receive iterators, it is a global function for all functions,
    there is a set of ways to traverse all the elements of the container uniformly
  3. His function is to provide a unified way to traverse the container transparently

Therefore, we need to implement the relevant operators in the iterator ourselves.

2. Implementation of the operator

(1) Provide an iterator implementation for the String type
Because it is an embedded type, it is written in the public of the string class.

Iterators also need to provide * operator overloading to access the values ​​of elements iterated by the iterator, and iterator dereferences access to the underlying data of the container.

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) The begin () method
returns a representation of the iterator that makes its underlying first element

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

(3) The end () method
returns a representation of the iterator at the subsequent position of the end element of the container

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

Second, implement the iterator of the vector container

Because vector is also a container, we can still use the above iterator,
(1) The specific implementation is as follows:

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) Three ways of iterator traversal

Method 1: Because vector is an array container, continuous space can be accessed using subscripts, but this method of traversal is only useful for vector

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

Method 2: Iterator's usual access method

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

Method three: a more convenient foreach method in c ++ 11 to traverse the values ​​of the internal elements of the container. The bottom layer is still traversed through the iterator

for (int val : vec)
	{
		cout << val << " ";
	}
	cout << endl;
Published 98 original articles · won praise 9 · views 3668

Guess you like

Origin blog.csdn.net/qq_43412060/article/details/105136908