【C++】vector的模拟实现

目录

  • 1.vector的结构
  • 2.构造函数
    • 2.1 无参构造
    • 2.2 以迭代器区间作为参数的构造函数
    • 2.3 构造n个value值
  • 3.拷贝构造
    • 3.1 传统写法
    • 3.2 现代写法
  • 4.赋值重载
  • 5.迭代器失效问题
    • 5.1 reserve和resize
    • 5.2 insert
  • 5.3 erase
  • 4. 整体代码(包含迭代器、析构函数等)

1.vector的结构

vector的结构体由三个迭代器类型组成,分别是:
指向第一个元素的_start,
指向最后一个元素下一个位置的_finish,
指向空间末尾下一个位置的_end_of_storage。

namespace lgr
{
	class vector
	{
	public:
		typedef T* iterator;
		typedef const T* const_iterator;
		......
	private:
		iterator _start;
		iterator _finish;
		iterator _end_of_storage;
	};
}

【C++】vector的模拟实现_第1张图片

2.构造函数

2.1 无参构造

无参构造可以直接用初始化列表将三个成员变量初始化为nullptr

vector()
	:_start(nullptr)
	,_finish(nullptr)
	,_end_of_storage(nullptr)
{ }

或者我们可以在成员变量声明时初始化,这样无参构造就不用写内容了

namespace lgr
{
	class vector
	{
	public:
		typedef T* iterator;
		typedef const T* const_iterator;
		
		vector()
		{ } 
		
	private:
		iterator _start = nullptr;
		iterator _finish = nullptr;
		iterator _end_of_storage = nullptr;
	};
}

2.2 以迭代器区间作为参数的构造函数

vector还支持用其他的容器来初始化,参数可以是vector类型的迭代器,也可以是其他容器类型的迭代器,所以需要用模板来实现。

template<class InputIterator>
vector(InputIterator first, InputIterator last)
{
	while (first != last)
	{
		push_back(*first);
		++first;
	}
}

2.3 构造n个value值

vector支持用n个value值进行构造,其中的value既可以是内置类型也可以是自定义类型,要注意v用引用做参数时要用const修饰,否则无法接收常量值。

vector(size_t n, const T& val = T())
{
	reserve(n);		// 后面会实现,先用着
	for (int i = 0; i < n; ++i)
	{
		push_back(val); 
	}
}

但是当我们实现好这个函数后,可以尝试以下构造一个n=5,val=1的vector,发现会报错,而构造n=5,val='a’的vector却正常,这是因为第一种构造匹配到了前面实现的以迭代器区间作为参数的构造函数,n和val的类型都是int,对于前面的函数模板InputIterator只需要进行一次类型推导,而在现在的构造函数中,int类型需要发生既需要发生类型推导,又要隐式类型转换成size_t,代价很大,所以就匹配到了以迭代器区间作为参数的构造函数。然后在该函数中对int类型解引用,所以会报非法的间接寻址。
解决方法就是再重载一个以int类型和常引用为参数的构造函数,这样当n和value都是int类型的时候,就会优先匹配到重载的函数。

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

3.拷贝构造

3.1 传统写法

我们可以直接new一块空间,然后把值拷贝进去。
首先,我们先用memcpy尝试一下:

vector(const vector<T>& v)
{
	_start = new T[v.size()];
	memcpy(_start, v._start, sizeof(T)*v.size());
	_finish = _start + v.size();
	_end_of_storage = _start + v.capacity();
}

代码很简单,但是当我们使用它时,有一个问题,就是当T的类型是string时拷贝,会发生崩溃。
这是因为memcpy是按字节拷贝,也就是浅拷贝,两个vector中的string指向同一块空间,当析构时会发生崩溃,所以这种方法不太行。
还可以用for循环,一个一个拷贝:

vector(const vector<T>& v)
{
	_start = new T[v.size()];
	for (size_t i = 0; i < v.size(); ++i)
	{
		_start[i] = v._start[i];	// 此时会调用string的赋值重载,发生的是深拷贝
	}
	_finish = _start + v.size();
	_end_of_storage = _start + v.capacity();
}

3.2 现代写法

利用构造函数创建临时变量tmp,然后将tmp和*this交换。

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

vector(const vector<T>& v)
{
	vector<T> tmp(v.begin(), v.end());
	swap(tmp);
}

4.赋值重载

赋值重载就和拷贝构造类似了,这里使用传值传参,并且不需使用临时变量tmp。

vector<T>& operator=(vector<T> v)
{
	swap(v);
	return *this;
}

5.迭代器失效问题

5.1 reserve和resize

void reserve(size_t n)  
{
	if (n > capacity())
	{
		size_t sz = size();
		T* tmp = new T[n];
		if (_start)
		{
			for (size_t i = 0; i < size(); ++i)
			{
				tmp[i] = _start[i];		// 调用赋值重载
			}
			delete[] _start;
		}
		_start = tmp;
		_finish = _start + sz;
		_end_of_storage = _start + n;
	}
}

void resize(size_t n, T val = T())
{
	if (n < size())		// 缩容
	{
		_finish = _start + n;
	}
	else
	{
		if (n > capacity())
		{
			reserve(n);
		}
		while (_finish != _start + n)
		{
			*_finish = val;
			++_finish;
		}
	}
}

5.2 insert

当我们使用insert时,需要传一个迭代器参数,写入时有可能会发生扩容,而根据上面的reserve,发生的是异地扩容,所以传进去的迭代器就失效了,现在其指向的是已经被释放的旧空间,如果坚持使用就会发生访问野指针。

void insert(iterator pos, const T& val)
{
	assert(pos >= _start);
	assert(pos <= _finish);
	if (_finish == _end_of_storage)
	{
		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 = val;
	++_finish;
}

void test8()
{
	std::vector<int> v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);
	std::vector<int>::iterator it = std::find(v.begin(), v.end(), 2);
	if (it != v.end())
	{
		v.insert(it, 10);
	}
	
	++(*it);	// 这里我们测试10是否会变成11
	for (auto e : v)
	{
		cout << e << " ";
	}
	cout << endl;
}

【C++】vector的模拟实现_第2张图片
解决方法:给insert添加一个返回值,返回新的迭代器

iterator insert(iterator pos, const T& val)
{
	assert(pos >= _start);
	assert(pos <= _finish);
	if (_finish == _end_of_storage)
	{
		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 = val;
	++_finish;
	return pos;
}

5.3 erase

iterator erase(iterator pos)
{
	assert(pos >= _start);
	assert(pos < _finish);
	iterator start = pos + 1;
	while (start != _finish)
	{
		*(start - 1) = *start;
		++start;
	}
	--_finish;
	return pos;
}

void test8()
{
	vector<int> v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);
	vector<int>::iterator pos = std::find(v.begin(), v.end(), 2);
	if (pos != v.end())
	{
		v.erase(pos);
	}
	// erase也会引发迭代器失效,这种情况下没有事,但是当pos指向vector最后一个元素时,就会导致erase后pos指向界外,在vs中不论在哪里erase后,再修改pos都会报错
	++(*pos);
	// 解决方法:
	// if (pos != v.end())
	// {
	// 	   pos = v.erase(pos);
	// }
	for (auto e : v)
	{
		cout << e << " ";
	}
	cout << endl;
}

4. 整体代码(包含迭代器、析构函数等)

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

		vector()
		{ }

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

		template<class InputIterator>
		vector(InputIterator first, InputIterator last)
		{
			while (first != last)
			{
				push_back(*first);
				++first;
			}
		}

		vector(const vector<T>& v)
		{
			vector<T> tmp(v.begin(), v.end());
			swap(tmp);
		}

		~vector()
		{
			delete[] _start;
			_start = _finish = _end_of_storage = nullptr;
		}

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

		vector<T>& operator=(vector<T> v)
		{
			swap(v);
			return *this;
		}


		void reserve(size_t n)  
		{
			if (n > capacity())
			{
				size_t sz = size();
				T* tmp = new T[n];
				if (_start)
				{
					//memcpy(tmp, _start, sizeof(T) * n);
					for (size_t i = 0; i < size(); ++i)
					{
						tmp[i] = _start[i];
					}
					delete[] _start;
				}
				_start = tmp;
				_finish = _start + sz;
				_end_of_storage = _start + n;
			}
		}

		void resize(size_t n, T val = T())
		{
			if (n < size())
			{
				_finish = _start + n;
			}
			else
			{
				if (n > capacity())
				{
					reserve(n);
				}
				while (_finish != _start + n)
				{
					*_finish = val;
					++_finish;
				}
			}
		}
		
		iterator insert(iterator pos, const T& val)
		{
			assert(pos >= _start);
			assert(pos <= _finish);
			if (_finish == _end_of_storage)
			{
				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 = val;
			++_finish;
			return pos;
		}

		iterator erase(iterator pos)
		{
			assert(pos >= _start);
			assert(pos < _finish);
			iterator start = pos + 1;
			while (start != _finish)
			{
				*(start - 1) = *start;
				++start;
			}
			--_finish;
			return pos;
		}

		void push_back(const T& val)
		{
			if (_finish == _end_of_storage)
			{
				reserve(capacity() == 0 ? 4 : 2 * capacity());
			}
			*_finish = val;
			++_finish;
		}
		void pop_back()
		{
			assert(!empty());
			--_finish;
		}
		const size_t capacity() const
		{
			return _end_of_storage - _start;
		}
		const size_t size() const
		{
			return _finish - _start;
		}
		bool empty() const
		{
			return _start == _finish;
		}
		iterator begin()
		{
			return _start;
		}
		iterator end()
		{
			return _finish;
		}
		const_iterator begin() const
		{
			return _start;
		}
		const_iterator end() const
		{
			return _finish;
		}
		T& operator[](size_t n)
		{
			return *(_start + n);
		}
	private:
		iterator _start = nullptr;
		iterator _finish = nullptr;
		iterator _end_of_storage = nullptr;
	};
}

你可能感兴趣的:(c++,开发语言)