【C++】STL-vector模拟实现

本文详细介绍了vactor的模拟实现,包括vector的构造、析构、拷贝构造、成员变量、容量管理、迭代器操作以及内存拷贝问题,重点讨论了C++中的vector类、Initializer_list的使用以及浅拷贝和资源管理的注意事项。
摘要由CSDN通过智能技术生成

目录

1、vactor的模拟实现

1.1 成员变量

1.2 size、capacity

1.3 迭代器

1.4 构造、析构、拷贝构造、operator=

1.5 push_back、pop_back、reserve

1.6 operator[]

1.7 insert、erase

1.8 resize

1.9 花括号初始化的问题(C++11支持)

2、使用memcpy拷贝问题


1、vactor的模拟实现

1.1 成员变量

vector是顺序表,但是并不像之前在数据结构中一样会有一个指针动态开辟出的空间的指针、size、capacity,而是由3个指针构成的

1.2 size、capacity

1.3 迭代器

1.4 构造、析构、拷贝构造、operator=

vector的构造函数当中有一个是使用迭代器区间进行初始化的

还有一个是使用n个相同的值来进行初始化

但此时会报错,原因是v4中的两个参数都是int,而n个相同值的构造中第一个是size_t,所以会匹配到迭代器区间初始化去了,因为迭代器区间初始化更加匹配

解决方法1:传参的时候在10的后面加个 u ,表明是size_t类型

解决方法2:重载一个n是int的n个相同值的构造函数

因为类对象会开辟空间,所以要自己实现深拷贝的拷贝构造和operatpr=

此时会有一个问题,就是拷贝构造中,范围for用的是引用,传到push_back中,push_back用的也是引用,若是自定义类型,这样不会造成浅拷贝吗?

不会的,因为push_back中的*_finish = x是会去调用对应类型的赋值运算符重载的

1.5 push_back、pop_back、reserve

此时这个reserve是有问题的

1.6 operator[]

1.7 insert、erase

此时也会有迭代器失效问题,因为如果需要增容,通过reserve增完后,_start、_finish、_endofstorage都指向了新的空间,而pos仍然指向原来的空间,所以需要更新pos的位置

1.8 resize

1.9 花括号初始化的问题(C++11支持)

在前面,我们有学习过单参数的构造函数可以使用隐式类型转换来进行初始化对象

实际上,对于多参数的构造函数,一样可以使用隐式类型转换来初始化对象

我们会发现vector也有类似的操作

此时,这也是一种隐式类型转换,但与上面类A的隐式类型转换有所不同,并不是直接用花括号内的数值去构造一个临时的vector,再赋值给v1,因为若是这样的话需要实现无穷多个不同参数个数的构造函数

在C++11中,引入了一个类模板initializer_list,支持将用{}括起来的任意多个值创建initializer_list对象

编译器会自动用被{}括起来的数据区实例化出一个initializer_list对象

initializer_list是支持迭代器的

initializer_list的成员变量就是两个指针,_Fist指针指向第一个值,_Last指针指向最后一个值的下一个

所以这个的隐式类型转换实际上是先用{}里面的内容去实例化出一个initializer_list对象,再用这个对象去构造一个vector,再拷贝构造,但通常会被优化成直接用initializer_list对象去构造v1

initializer_list在使用范围for遍历时,不用加引用

{}的用法:1. 单参数、多参数的隐式类型转换,单参数通常不用

                 2. initializer_list的构造

2、使用memcpy拷贝问题

在上面的reserve中使用了memcpy,在遇到自定义类型时会出错,因为memcpy是浅拷贝

问题分析:
1. memcpy是内存的二进制格式拷贝,将一段内存空间中内容原封不动的拷贝到另外一段内存空间中
2. 如果拷贝的是内置类型的元素,memcpy既高效又不会出错,但如果拷贝的是自定义类型元素,并且自定义类型元素中涉及到资源管理时,就会出错,因为memcpy的拷贝实际是浅拷贝。
结论:如果对象中涉及到资源管理时,千万不能使用memcpy进行对象之间的拷贝,因为memcpy是浅拷贝,否则可能会引起内存泄漏甚至程序崩溃

上面的tmp[i]=_start[i]也不一定是string的operator=,应该是T的operator=,只是这里是string的

3、进一步理解迭代器失效

会发现STL库中的vector的迭代器是一个类,并不是单纯的是一个T*的指针,它是用了一个类去封装T*,里面会有一个flag来记录迭代器是否失效,当erase后,将flag=0,在操作迭代器时,若flag==0,则会报错,因为erase可能会有缩容等等可能造成野指针的情况,所以为了绝对安全,只要erase了,就直接让flag=0。Linux下不一样是因为类中的操作不同。 

#include<iostream>
#include<cassert>
using namespace std;
template<class T>
class Vector
{
public:
	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;
	}
	Vector()
		:_start(nullptr)
		,_finish(nullptr)
		,_endofstorage(nullptr)
	{}
	~Vector()
	{
		delete[] _start;
		_start = _finish = _endofstorage = nullptr;
	}
	/*Vector(const Vector<T>& v)
	{
		_start = new T[v.capacity()];
		_finish = _start;
		_endofstorage = _start + capacity();
		for (size_t i = 0; i < v.size(); i++)
		{
			*_finish = v[i];
			_finish++;
		}
	}*/
	/*Vector(const Vector<T>& v)
	{
		_start = new T[v.capacity()];
		_finish = _start + v.size();
		_endofstorage = _start + v.capacity();
		memcpy(_start, v._start, sizeof(T) * v.size());
	}*/
	Vector(const Vector<T>& v)
		:_start(nullptr)
		, _finish(nullptr)
		, _endofstorage(nullptr)
	{
		reserva(v.capacity());//防止push_back频繁扩容
		for (const auto& e : v)
			push_back(e);
	}
	//Vector<T>& operator=(const Vector<T>& v)
	//{
	//	if (this != &v)
	//	{
	//		delete[] _start;
	//		_start = new T[v.capacity()];
	//		memcpy(_start, v._start, sizeof(T) * v.size());
	//		//也可以一个一个赋值
	//	}
	//	return *this;
	//}
	void swap(Vector<T>& v)
	{
		::swap(_start, v._start);
		::swap(_finish, v._finish);
		::swap(_endofstorage, v._endofstorage);
		//这里是在局部调用全局的swap,所以前面加::
	}
	Vector<T>& operator=(Vector<T> v)
	{
		swap(v);
		return *this;
	}
	//for (size_t i = 0; i < sz; i++)
	//{
	//	tmp[i] = _start[i];//利用string的operator=的深拷贝
	//}
	void reserve(size_t n)
	{
		if (n > capacity())
		{
			size_t sz = size();//先记录_finish相对_start的偏移量
			T* tmp = new T[n];
			if (_start)
			{
				注意这里一定要判断一下,当_start是空的时候使用memcoy会报错
				且因为_start是空,也没必要将上面的内容拷贝到tmp上
				memcpy(tmp, _start, sizeof(T) * size());
				delete[] _start;
			}
			_start = tmp;
			_finish = _start + sz;
			_endofstorage = _start + n;
		}
	}
	void push_back(const T& x)//用引用是因为T可能是自定义类型
	{
		if (_finish == _endofstorage)//满了则扩容
		{
			size_t newcapacity = capacity() == 0 ? 2 : capacity() * 2;
			reserve(newcapacity);
		}
		*_finish = x;
		++_finish;
		//insert(_finish, x);也可以直接复用insert
	}
	void pop_back()
	{
		assert(_start < _finish);
		//保证不为空
		_finish--;
		//erase(_finish-1);//也可复用erase
	}
	void insert(iterator pos, const T& x)
	{
		assert(pos <= _finish);
		if (_finish == _endofstorage)
		{
			size_t n = pos - _start;//计算pos相对_start的偏移量
			size_t newcapacity = capacity() == 0 ? 2 : capacity() * 2;
			reserve(newcapacity);
			pos = _start + n;//更新pos的位置
		}
		iterator end = _finish - 1;
		while (end >= pos)
		{
			*(end + 1) = *end;
			end--;
		}
		*pos = x;
		_finish++;
	}
	iterator erase(iterator pos)
	{
		assert(pos < _finish);//确保删除位置有效
		iterator it = pos;
		while (it < _finish)
		{
			*it = *(it + 1);
			it++;
		}
		_finish--;
		return pos;
		//删除后原本的下一个就到了pos的位置
		//所以返回迭代器就是返回原来的位置
	}
	void resize(size_t n, const T& val = T())
	{
		//上面给的缺省值不能给0,因为不一定是int类型,T()是针对任何类型的
		//int i = int();  ->  i=0;
		//int i = int(7)  ->  i=7;
		//C++为了让内置类型可以兼容模板
		if (n <= size())//如果比size还小或等于,不用填充,直接缩小
		{
			_finish = _start + n;
		}
		else//需要填充
		{
			if (n > capacity())//扩容
			{
				reserve(n);
			}
			//填充,从_finish的位置开始向后填充
			//填充时不能用memcpy将val直接弄到_finish到_start+n的位置
			//因为memcpy是浅拷贝,若是自定义类型且开辟了空间
			//会直接让所以的都指向同一块空间
			while (_finish < _start + n)
			{
				*_finish = val;
				_finish++;
			}
		}
	}
	T& operator[](size_t i)//可读可写
	{
		assert(i < size());//断言,防止i越界
		return _start[i];
	}
	const T& operator[](size_t i) const//只可读
	{
		assert(i < size());//断言,防止i越界
		return _start[i];
	}
	size_t size() const
	{
		return _finish - _start;
	}
	size_t capacity() const
	{
		return _endofstorage - _start;
	}
private:
	iterator _start;//指向动态开辟数组的起始位置
	iterator _finish;//指向动态开辟数组的有值的下一个位置
	iterator _endofstorage;//指向动态开辟数组的末尾
};

升级版

#pragma once
#include<iostream>
#include<algorithm>
#include<assert.h>
#include<initializer_list>
namespace cxf
{
	template<class T>
	class vector
	{
	public:
		//迭代器相关
		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;
		}


		// 默认成员函数
		// 普通的构造函数
		vector()
			:_start(nullptr)
			, _finish(nullptr)
			, _end_of_storage(nullptr)
		{}
		// 构造函数还可以使用迭代器区间进行初始化
		// 在类模板的内部也可以定义类函数
		// 写成函数模板是为了支持各种类型的迭代器
		// 若是用iterator则只支持vector的迭代器
		template <class InputIterator>
		vector(InputIterator first, InputIterator last)
		{
			while (first != last)
			{
				push_back(*first);
				first++;
			}
		}
		// 构造函数还支持用n个相同的值来进行初始化
		// T()是一个匿名对象,会去调用T的构造函数
		// C++对内置类型进行了升级,也有了构造函数
		// int i = 1;
		// int j(1);
		// int k = int();
		// int x = int(1);
		vector(size_t n, const T& x = T())
		{
			reserve(n);
			for (size_t i = 0; i < n; i++)
				push_back(x);
		}
		vector(int n, const T& x = T())
		{
			reserve(n);
			for (int i = 0; i < n; i++)
				push_back(x);
		}
		// 使用花括号的构造函数
		vector(std::initializer_list<T> il)
		{
			reserve(il.size());
			for (auto e : il)
				push_back(e);
		}
		~vector()
		{
			delete[] _start;
			_start = _finish = _end_of_storage = nullptr;
		}
		// 传统写法的拷贝构造
		/*vector(const vector<T>& v)
		{
			T* tmp = new T[v.capacity()];
			for (size_t i = 0; i < v.size(); i++)
			{
				tmp[i] = v._start[i];
			}
			_start = tmp;
			_finish = tmp + v.size();
			_end_of_storage = tmp + v.capacity();
		}*/
		// 现代写法的拷贝构造
		vector(const vector<T>& v)
			:_start(nullptr)
			, _finish(nullptr)
			, _end_of_storage(nullptr)
		{
			reserve(v.capacity());
			for (auto e : v)
				push_back(e);
		}
		// 传统写法的赋值运算符重载
		/*vector& operator=(const vector<T>& v)
		{
			if (_start == nullptr)
				delete[] _start;
			T* tmp = new T[v.capacity()];
			for (size_t i = 0; i < v.size(); i++)
			{
				tmp[i] = v._start[i];
			}
			_start = tmp;
			_finish = tmp + v.size();
			_end_of_storage = tmp + v.capacity();
			return *this;
		}*/
		// 现代写法的赋值运算符重载
		vector& operator=(vector<T> v)
		{
			swap(v);
			return *this;
		}


		// 其他函数
		T& operator[](size_t i)
		{
			assert(i < size());
			return _start[i];
		}
		const T& operator[](size_t i) const
		{
			assert(i < size());
			return _start[i];
		}
		void swap(vector<T>& v)
		{
			std::swap(_start, v._start);
			std::swap(_finish, v._finish);
			std::swap(_end_of_storage, v._end_of_storage);
		}
		size_t size() const
		{
			return _finish - _start;
		}
		size_t capacity() const
		{
			return _end_of_storage - _start;
		}
		void reserve(size_t n)
		{
			assert(n > capacity());
			size_t old_size = size();
			T* tmp = new T[n];
			for (int i = 0; i < old_size; i++)
			{
				tmp[i] = _start[i];
			}
			delete[] _start;
			_start = tmp;
			_finish = _start + old_size;
			_end_of_storage = _start + n;
		}
		void resize(size_t n, const T& val = T())
		{
			//上面给的缺省值不能给0,因为不一定是int类型,T()是针对任何类型的
			//int i = int();  ->  i=0;
			//int i = int(7)  ->  i=7;
			//C++为了让内置类型可以兼容模板
			if (n <= size())//如果比size还小或等于,不用填充,直接缩小
			{
				_finish = _start + n;
			}
			else//需要填充
			{
				if (n > capacity())//扩容
				{
					reserve(n);
				}
				//填充,从_finish的位置开始向后填充
				//填充时不能用memcpy将val直接弄到_finish到_start+n的位置
				//因为memcpy是浅拷贝,若是自定义类型且开辟了空间
				//会直接让所以的都指向同一块空间
				while (_finish < _start + n)
				{
					*_finish = val;
					_finish++;
				}
			}
		}
		void push_back(const T& x)
		{
			if (_finish == _end_of_storage)
			{
				size_t newcapacity = size() == 0 ? 4 : capacity() * 2;
				reserve(newcapacity);
			}
			*_finish = x;
			_finish++;
		}
		void pop_back()
		{
			assert(_finish != _end_of_storage);
			_finish--;
		}
		void insert(iterator pos, const T& x)
		{
			assert(pos <= end());
			if (_finish == _end_of_storage)
			{
				size_t newcapacity = size() == 0 ? 4 : capacity() * 2;
				reserve(newcapacity);
			}
			iterator it = _finish;
			while (it >= pos)
			{
				*it = *(it - 1);
				--it;
			}
			*pos = x;
			_finish++;
		}
		iterator erase(iterator pos)
		{
			assert(pos <= end());
			if (pos == end())
				_finish--;
			else
			{
				iterator it = pos;
				while (it < end())
				{
					*it = *(it + 1);
					it++;
				}
			}
			_finish--;
			return pos;
		}
	private:
		iterator _start;
		iterator _finish;
		iterator _end_of_storage;
	};
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值