C++: Simulation implementation of vector and vector iterator invalidation and copying problems

Implemented functions

insert image description here

Simulation implementation

Since the string was implemented earlier, the implementation process is not the focus here, and the focus is on the iterator invalidation and copying issues

template <class T>
class vector
{
    
    
public:
	typedef T* iterator;
	typedef const T* const_iterator;

	// constructor
	vector()
		:_start(nullptr)
		, _finish(nullptr)
		, _endofstorge(nullptr)
	{
    
    }

	template <class InputIterator>
	vector(InputIterator first, InputIterator last)
		: _start(nullptr)
		, _finish(nullptr)
		, _endofstorge(nullptr)
	{
    
    

		while (first != last)
		{
    
    
			push_back(*first);
			first++;
		}
	}

	vector(size_t n, const T& val = T())
	{
    
    
		reserve(n);
		for (int i = 0; i < n; i++)
		{
    
    
			push_back(val);
		}
	}

	vector(int n, const T& val = T())
	{
    
    
		reserve(n);
		for (int i = 0; i < n; i++)
		{
    
    
			push_back(val);
		}
	}

	vector(vector<T>& v)
	{
    
    
		reserve(v.capacity());
		for (auto& e : v)
		{
    
    
			push_back(e);
		}
	}

	// destructor
	~vector()
	{
    
    
		delete[] _start;
		_start = _finish = _endofstorge = nullptr;
	}

	// operator=
	vector<T>& operator=(vector<T>& v)
	{
    
    
		//_start = new T[v.capacity()];
		//_finish = _start + v.size();
		//_endofstorge = _start + v.capacity();
		swap(v);
		return *this;
	}

	// capacity
	size_t size()
	{
    
    
		return _finish - _start;
	}
	void resize(size_t num, const T& val = T())
	{
    
    
		if (num < size())
		{
    
    
			_finish = _start + num;
		}
		else
		{
    
    
			while (_finish != _start + num)
			{
    
    
				push_back(val);
				_finish++;
			}
		}
	}
	size_t capacity()
	{
    
    
		return _endofstorge - _start;
	}
	void reserve(size_t num)
	{
    
    
		if (num > capacity())
		{
    
    
			T* tmp = new T[num];
			size_t sz = size();
			if (_start)
			{
    
    
				for (size_t i = 0; i < sz; i++)
				{
    
    
					tmp[i] = _start[i];
				}

				delete[] _start;
			}

			_start = tmp;
			_finish = _start + sz;
			_endofstorge = _start + num;
		}
	}
	bool empty()
	{
    
    
		if (_start == _finish)
			return true;
		return false;
	}

	// element access
	T& operator[](size_t pos)
	{
    
    
		return *(_start + pos);
	}

	// iterator
	iterator begin()
	{
    
    
		return _start;
	}
	iterator end()
	{
    
    
		return _finish;
	}
	const_iterator begin() const
	{
    
    
		return _start;
	}
	const_iterator end() const
	{
    
    
		return _finish;
	}

	// modifier
	void push_back(T data)
	{
    
    
		if (_finish == _endofstorge)
		{
    
    
			size_t sz = size();
			size_t newcapacity = capacity() == 0 ? 4 : capacity() * 2;
			T* tmp = new T[newcapacity];

			if (_start)
			{
    
    
				//memcpy(tmp, _start, sizeof(T) * size());
				for (size_t i = 0; i < sz; i++)
				{
    
    
					tmp[i] = _start[i];
				}
				delete[] _start;
			}

			_finish = tmp + sz;
			_endofstorge = tmp + newcapacity;
			_start = tmp;
		}

		assert(_finish);
		*(_finish) = data;
		++_finish;
	}

	void pop_back()
	{
    
    
		--_finish;
	}

	void insert(iterator pos, const T& x)
	{
    
    
		assert(pos >= _start);
		assert(pos <= _finish);

		if (_finish == _endofstorge)
		{
    
    
			size_t len = pos - _start;
			reserve(capacity() == 0 ? 4 : capacity() * 2);
			pos = _start + len;
		}

		iterator end = _finish - 1;
		while (end >= pos)
		{
    
    
			*(end + 1) = *end;
			--end;
		}

		*pos = x;
		++_finish;
	}

	iterator erase(iterator pos)
	{
    
    
		assert(pos >= _start);
		assert(pos < _finish);

		iterator it = pos + 1;
		while (it < _finish)
		{
    
    
			*(it - 1) = *it;
			++it;
		}

		--_finish;

		return pos;
	}


	void swap(vector<T>& v)
	{
    
    
		std::swap(_start, v._start);
		std::swap(_finish, v._finish);
		std::swap(_endofstorge, v._endofstorge);
	}

private:
	iterator _start;
	iterator _finish;
	iterator _endofstorge;
};

iterator invalidation

First of all, it is necessary to be clear about what is an iterator invalidation, and where is the application scenario of an iterator invalidation?

Here is an example. The iterator may fail during the insert process. The specific reason for the failure is:

The iterator itself can be understood as a pointer and the space pointed to by the pointer is fixed, so when using a pointer here, if the original space is destroyed due to expansion, then the content pointed to by the iterator is actually the same It doesn't make any sense, which is why the iterator fails

It will be easier to understand with the following example:

void test_vector1()
{
    
    
	vector<int> v(5,5);

	auto it = v.begin();

	v.insert(it, 10);
	v.insert(it, 10);
	v.insert(it, 10);
	v.insert(it, 10);
	v.insert(it, 10);

	for (auto e : v)
	{
    
    
		cout << e << " ";
	}
	cout << endl;
}

insert image description here

A vector is defined here, and there are 5 numbers 5 in it. At this time, _startthe space pointed to is an area, so the head of the container can be found by using an iterator here, and it is very convenient to insert

insert image description here

But when the data is inserted, _startthe space pointed to changes, but the iterator here still points to the original position. In this case, it is a classic iterator failure problem. Therefore, under some compilers, the standard library If the iterator is insert/eraseused, the iterator must be checked forcibly and cannot be used

Under some compilers, the check will not be performed, so the iterator will fail. Based on this situation, when using the iterator, pay attention to whether there is a possibility of failure

Implicit shallow copy problem

First look at the following code

	void push_back(T data)
	{
    
    
		if (_finish == _endofstorge)
		{
    
    
			size_t sz = size();
			size_t newcapacity = capacity() == 0 ? 4 : capacity() * 2;
			T* tmp = new T[newcapacity];

			if (_start)
			{
    
    
				//memcpy(tmp, _start, sizeof(T) * size());
				for (size_t i = 0; i < sz; i++)
				{
    
    
					tmp[i] = _start[i];
				}
				delete[] _start;
			}

			_finish = tmp + sz;
			_endofstorge = tmp + newcapacity;
			_start = tmp;
		}

		assert(_finish);
		*(_finish) = data;
		++_finish;
	}

If you memcpyhave doubts about whether it can be used, you can see the following test function

void test_vector1()
{
    
    
	vector<string> v;
	v.push_back("111111111111111111111");
	v.push_back("111111111111111111111");
	v.push_back("111111111111111111111");
	v.push_back("111111111111111111111");
	v.push_back("111111111111111111111");

	for (auto e : v)
	{
    
    
		cout << e << " ";
	}
	cout << endl;
}

If memcpythe result of using is like this

insert image description here

The reason for such a result is also very simple. This is because it memcpyis actually a shallow copy in essence. Its working principle is to copy the content of each byte, so this is actually a shallow copy. Use the following mechanism To explain it can be seen as: insert image description here
Therefore, we have adopted an improved version of the copy mechanism to allow these data to be copied to a certain extent

insert image description here
In this way, the effect of copying can be achieved

Guess you like

Origin blog.csdn.net/qq_73899585/article/details/132198073