【C++】vector模拟实现

目录

1.构造函数

(1)无参构造

(2)带参构造函数

(3)用迭代器构造初始化函数

(4)拷贝构造函数 

 2.operator=

3.operator[]

4.size()

5.capacity()

6.push_back

 7.reserve

 8.迭代器(vector的原生指针)

9.resize

10.pop_back

11.insert

12.erase

13.memcpy替换为深拷贝


 大致框架

namespace zbw
{
	template <class T>
	class vector
	{
	public:
		typedef T* iterator;
	private:
		iterator _start;
		iterator _finish;
		iterator _end_of_storage;
	};
}

1.构造函数

(1)无参构造

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

(2)带参构造函数

	vector(size_t n,const T& val = T())//const延长匿名对象的生命周期
		:_start(nullptr)
		, _finish(nullptr)
		, _end_of_storage(nullptr)
	{
		reverse(n);
		for (int i = 0; i < n; i++)
		{
			push_back(val);
		}
	}

(3)用迭代器构造初始化函数

	tmplete <class InputIterator>
	vector(InputIterator first,InputIterator last)
		:_start(nullptr)
		, _finish(nullptr)
		, _end_of_storage(nullptr)
	{
		while (first != last)
		{
			push_back(*first);
			first++;
		}
	}

使用迭代器模板,就可以传任意类型的迭代器,否则只能使用vector的迭代器

起名为InputIterator,是因为函数模板的模板参数要传迭代器区间时,是存在命名规范的

InputIterator这种迭代器所指的对象为“只读”,不允许外界更改

(4)拷贝构造函数 

传统写法

	vector(const vector<T>& v)//v2(v1)
	{
		_start = new T[v.capacity()];
		_finish = _start + v._size();
		_end_of_storage = _start + v._capacity;

		memcpy(_start, v._start, v.size() * sizeof(T));
	}

开一块和v1一样大的空间,再把v1的数据拷贝到新空间里去

现代写法 

	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)//v2(v1)
	{
		vector<T> tmp(v.begin(), v.end());
		swap(tmp);
	}

 利用迭代器初始化的构造函数,用v1创造一个临时变量tmp,再将v2与tmp交换,tmp出了作用域就调用析构函数销毁

 2.operator=

现代写法

	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=(const vector<T> v)//v3 = v1
	{
		swap(v);
		return *this;
	}

利用传值传参拷贝构造v(此时v就是v1),再将v3与v1交换,v是临时对象除了作用域会调用析构函数销毁

3.operator[]

	T& operator[](size_t i)
	{
		assert(i < size());
		return _start[i];//_start的类型是T*,T是模板,所返回值是T&
	}
	const T& operator[](size_t i) const
	{
		assert(i < size());
		return _start[i];
	}

 const版本适合const类型的对象传参,且只能读不能写

4.size()

	size_t size() const
	{
		return _finish - _start;
	}

5.capacity()

	size_t capacity() const
	{
		return _end_of_storage - _size;
	}

6.push_back

首先判断容量是否足够,不够就进行增容

然后如果是空数组,就直接在_finish的位置上插入

如果数组不为空且容量不足,就将数据拷贝到扩容后的新空间,再在_finish的位置插入

	void push_back(const T& x)
	{
		if (_finish == _end_of_storage)
		{
			size_t newcapacity = capacity() == 0 ? 4 : capacity() * 2;
			T* tmp = T[newcapacity];
			if (_start)
			{
				memcpy(tmp, _start, sizeof(T) * size());
				delete[] _start;
			}
			_start = tmp;
		}
		*_finish = x;
		_finish++;
	}

 但是代码会崩溃,因为_finish和_end_of_storage无参构造之后是空指针,所以我们需要借助tmp算出_finish和_end_of_storage

	void push_back(const T& x)
	{
		if (_finish == _end_of_storage)
		{
			size_t newcapacity = capacity() == 0 ? 4 : capacity() * 2;
			T* tmp = T[newcapacity];
			if (_start)
			{
				memcpy(tmp, _start, sizeof(T) * size());
				delete[] _start;
			}
			_start = tmp;
			_finish = _start + size();//算出_finish的位置
			_end_of_storage = _start + newcapacity;//算出_end_of_storage的位置
		}
		*_finish = x;
		_finish++;
	}

 但是又因为我们实现的size()是_finish - _start,所以_finish=_start+ _finishi - _start = _finish

相互抵消之后,_finish永远是个空指针

 最终版本(提前准备好size,再去更新_start)

	void push_back(const T& x)
	{
		if (_finish == _end_of_storage)
		{
			size_t newcapacity = capacity() == 0 ? 4 : capacity() * 2;
			T* tmp = T[newcapacity];
			size_t sz = size();//提前算出size
			if (_start)//等于空则不需要拷贝
			{
				memcpy(tmp, _start, sizeof(T) * size());
				delete[] _start;
			}
			_start = tmp;
			_finish = _start + sz;
			_end_of_storage = _start + newcapacity;
		}
		*_finish = x;
		_finish++;
	}

 7.reserve

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

如果输入的数量大于capacity就扩容

然后我们可以将reserve复用到push_back里

	void push_back(const T& x)
	{
		if (_finish == _end_of_storage)
		{
			reserve(capacity() == 0 ? 4 : capacity() * 2);
		}
		*_finish = x;
		_finish++;
	}

 8.迭代器(vector的原生指针)

	typedef T* iterator;
	typedef const T* const_iterator;
	iterator begin()
	{
		return _start;
	}
	iterator end()
	{
		return _finish; //最后一个数据的下一个位置
	}

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

9.resize

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

 val的缺省值不能=0,因为val不一定是整形,还可能是其他类型

所以就用匿名对象,并用const延长它的生命周期

10.pop_back

	void pop_back()
	{
		assert(_finish > _start)
		_finish--;
	}

 当_finish == _start时,就没有数据了

11.insert

	void insert(iterator pos,const T& x)
	{
		assert(pos <= _finish)
		assert(pos >= _start)
		if (_finish == _end_of_storage)
		{
			reserve(capacity() == 0 ? 4 : capacity() * 2);
		}
		iterator end = _finish - 1;
		while (end >= pos)
		{
			*(end - 1) = *end;
			end--;
		}
		*pos = x;
		_finish++;
	}

但是会出现迭代器失效的问题

在插入时进行若进行扩容,会开辟一个新空间,这时_start和_finish都改变了指向

当原空间被释放后,pos指向了一个被释放的空间,变成了野指针

解法:

        可以提前算好pos与_start之间的距离

 但是我们调用insert函数时,传的是实参

vector<int>::iterator it = find(v.begin(), v.end(), 2);
if (it != end())
{
	v.insert(it, 12);
}

如果之后我们还要使用这个it,那么这个it就是失效

因为我们写的insert的实现是传值返回,形参的改变不影响实参

如果insert中发生了扩容,就会导致it指向空间被释放,让it成为一个野指针,这也叫做叫做迭代器失效

解法:

        用一个返回值接收pos

	iterator insert(iterator pos,const T& x)
	{
		assert(pos <= _finish)
		assert(pos >= _start)
		if (_finish == _end_of_storage)
		{
			//扩容会导致pos的失效,扩容前需要更新一下pos
			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++;

		return pos;
	}

调用insert时

vector<int>::iterator it = find(v.begin(), v.end(), 2);
if (it != end())
{
	it = v.insert(it, 12);
}

若将v.insert(it,12)改为v.insert(v.begin(),12)

并iterator insert(iterator pos,const T& x)改为iterator insert(iterator& pos, const T& x)

这样做是不行

因为begin()是传值返回,返回的是临时变量,具有常性,不能直接传给引用

12.erase

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

		iterator begin = pos + 1;
		while (begin < _finish)
		{
			*(begin - 1) = *begin;
			++begin;
		}
		--_finish;
	}
vector<int>::iterator it = v.begin;
while(it != v.end)
{
    if(*it % 2 == 0)//删除所有偶数
    {
        v.erase(it);
    }
    it++;
}

若我们要求删除数据1234中的所有偶数,代码就会出错

因为如果最后一个数是偶数,会导致erase之后it再次++,这会导致it错过_finish,导致越界

erase(it)后,it指向位置的意义就改变了,如果是连续的偶数,会导致后一个偶数没有被判断,也没有被删掉

erase(it)后it的意义改变了,本质上就是it失效了

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

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

对erase增加一个返回值,返回删除后pos的位置

vector<int>::iterator it = v.begin;
while(it != v.end)
{
    if(*it % 2 == 0)//删除所有偶数
    {
        it = v.erase(it);
    }
    else
    {
        it++;
    }
}

13.memcpy替换为深拷贝

之前实现的push_back

	void push_back(const T& x)
	{
		if (_finish == _end_of_storage)
		{
			size_t newcapacity = capacity() == 0 ? 4 : capacity() * 2;
			T* tmp = T[newcapacity];
			if (_start)
			{
				memcpy(tmp, _start, sizeof(T) * size());
				delete[] _start;
			}
			_start = tmp;
		}
		*_finish = x;
		_finish++;
	}

我们写的push_back拷贝用的是memcpy,对于内置类型,tmp出了作用域也不用处理,但对于自定义类型,tmp除了作用域还要被析构一次,一个空间总共析构了两次,程序崩溃

解决方法:

        不用memcpy进行浅拷贝,而是进行深拷贝

        我们直接更改reserve,再在push_back里复用

	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 < sz; i++)
                {
				    tmp[i] = _start[i];
				}
				delete[] _start;
			}
			_start = tmp;
			_finish = _start + sz;
			_end_of_storage = _start + n;
		}
	}

如果T是自定义类型,拷贝调用的是T的深拷贝赋值

    void push_back(const T&x)
	{
		if (_finish == _end_of_storage)
		{
			reserve(capacity() == 0 ? 4 : capacity() * 2);
		}
		*_finish = x;
		++_finish;
    }    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值