【C++】vector容器的模拟实现

vector容器实际是一个动态数组,它与array的操作非常相似。最大的区别就在于vector的“动态”,动态的理解就是数组的扩容由vector内部机制实现动态开辟空间,无需用户手动操作(申请新空间-复制元素-释放原空间)。
下面我将通过对vector的理解,自己模拟实现vector的基本操作方法,供大家更好的理解这个容器。

下面是vector函数实现的思想,最下面附有笔者完整代码和测试类,有需要的自取~

1.定义类模板,扩展vector的使用

首先我们要知道,vector数组中可存放的数据类型有很多,例如常见的int,float,string 都可以用vector进行操作。但我们如果针对每一个数据类型写一个容器的实现,不止会有很多重复的代码,同样也失去了“容器”的意义。所以下面这段代码不属于vector的方法,但却是我们不可忽略的。

template<class T>  //定义

//类模板的使用实例
Vector<int> v1;  //v1中存储的类型为int
Vector<std::string> v;  //v中存储的类型为std空间的string

笔记: 上面这段代码,相信大家在很多地方都见到过,可是它到底是怎么用的呢?
由于类模板包含类型参数,因此又称为参数化的类。如果说类是对象的抽象,对象是类的实例,则类模板是类的抽象,类是类模板的实例。概念理解了,下面是如何使用的简单介绍。

1.我们在类声明前加入该行代码,如下:
template < class T> //注意本行末尾无分号
class vector
{…}; //类体
2.使用类模板定义对象时,尖括号内写入实际类型名,如下:
类模板名<实际类型名> 对象名;
类模板名<实际类型名> 对象名(实参表列);
3.在类模板外定义成员函数的方法如下:
函数类型 类模板名<虚拟类型参数>::成员函数名(函数形参表列) {…}
4.类模板的类型参数可以有一个或多个,但每个类型前面都必须加class,如:
template <class T1,class T2>
class someclass
{…};
someclass<int,double> obj; //定义对象时代入实际类型名即可

2.无参构造函数

vector无参构造函数的作用是用户未传参时,对象也能被正确的创建出来,此时元素个数是0。

 		Vector()            		
 			:_start(nullptr)
 			, _finish(nullptr)
 			, _endOfStorage(nullptr)
 		{} // 等同于Vector() = default;

3.拷贝构造函数

拷贝构造函数,作用是创建一个与v1数组相同的copy数组对象。使用示例:vector < int > copy(v2); 或者vector < int > copy = v2;
注意: vector即动态数组的拷贝构造函数实现应该用深拷贝。深浅拷贝的区别可以参考博客深浅拷贝的认识

  Vector(const Vector<T>& v)      
    		{
    			_start = new T[v.Capacity()];
    			//memcpy(_start, v._start, sizeof(T)*v.Size());  //这里是浅拷贝,不能满足
    			for (size_t i = 0; i < v.Size(); ++i)
    			{
    				_start[i] = v._start[i];   //每个值挨着复制到新数组中
    			}
    			_finish = _start + v.Size();
    			_endOfStorage = _start + v.Capacity();
    		}

思考: 为什么拷贝构造函数必须用深拷贝实现呢?

答:如下图所示的区别,浅拷贝时仅仅改变了copy数组的指针方向,那么数组v2和copy在被析构时就会出现"v2申请的空间"被释放两次,这显然是不合理的,并且会造成程序崩溃。
而我们用深拷贝实现就能完美避免这样的问题出现,此时每个空间只会被释放一次。
在这里插入图片描述

4.赋值运算符 = 重载

通过重载赋值运算符,我们就可以实现数组的 = 赋值操作,例如 v3 = v2; 使得v3的数组元素与v2相同。

Vector <T>& operator =(Vector <T> v)   //形参   
    		{
    			Swap(v);  //将参数中数组的值
    			return *this;
    		}
    		void Swap(Vector <T> &v)
    		{
    			std::swap(_start, v._start);
    			std::swap(_finish, v._finish);
    			std::swap(_endOfStorage, v._endOfStorage);
    		}

思考: 赋值运算符的实现为什么不需要将形参中的值依次拷贝到新数组中?

答:形式参数是一个临时对象,在函数调用结束后,析构函数会自动处理形参对象,故此时指向“V2内存空间”的只有一个指针,无需再重新开辟空间。

在这里插入图片描述

5.析构函数

vector的析构函数,将实现申请的空间自动释放的功能。

    ~Vector()   
    		{
    			if (_start)
    			{
    				delete[] _start;
    				_start = _finish = _endOfStorage = nullptr;
    			}
    		}

6.扩容函数

vector的扩容方法有两个,其中reserve() 可以扩充数组的容量大小,resize() 可以扩充数组元素个数。
要理解这两个函数的区别,我们需要清楚capacity不等于size,capacity容量是指容器在分配新存储空间之前可以存储的元素总数,而size是指容器当前可使用的元素个数;

打个比方,reserve(5)表示私家车的容量是5个座位但不是一定有5个座位,而resize(4)表示设置了4个座位。换个说法的理解就是私家车里最多可以设置5个座位,但是当前只设置了4个座位所以只能用4个座位,可以继续通过resize(1)再增加一个座位。
区别也可参考文档STL容器的reserve()函数和resize()函数解析

注意:
1.实际我们在使用vector时,resize()使用的频率更高一些,因为当size>capacity时,resize()会自动调用reserve()实现扩容;而capacity的扩容函数reserve()不会使可用的元素个数增加。
2.reserve()函数扩容步骤:开辟新空间 – 复制元素 – 释放原空间;reserve()函数一般作为其他方法实现时调用的底层函数使用,无需单独调用。
3.resize(n,a)扩容可以设置填充元素的默认值为a。

 void Reserve(size_t n)   //容量的扩充,实际可用元素个数不变
    		{
    			if (n > Capacity())
    			{
    				size_t size = Size();
    				T* tmp = new T[n];  //1.开辟新空间
    				if (_start)
    				{
    					for (size_t i = 0; i < size; i++)
    					{
    						tmp[i] = _start[i];  //2.复制元素
    					}
    					delete[] _start;  //3.释放原空间
    				}
    				_start = tmp;  //新空间指针重定义
    				_finish = _start + size;
    				_endOfStorage = _start + n;
    			}
    		}
    
void Resize(size_t n, const T& value = T())  //可用元素个数的扩充
    		{
    			if (n <= Size())    //预扩充个数n比原来总元素Size()少,可能删减元素
    			{
    				_finish = _start + n;
    				return;
    			}
    			else
    			{
    				if (n > Capacity())  //预扩充个数n比容量大,则调用Reserve(n)增容
    				{
    					Reserve(n);
    				}
    				while (_finish != _start + n)   //预扩充个数n比容量小,扩展可使用的个数
    				{
    					*_finish = value;
    					++_finish;
    				}
    			}
    		}

7.插入函数

insert()插入操作对vector来说,是效率很低的一件事情,因为vector要求元素存储空间是连续的,所以我们每插入一次都意味着有元素的挪动。效率较高的是PushBack()即尾插,可以不移动其他元素位置直接插入。
增容可能引起迭代器的失效。

 void PushBack(const T&x)  //尾插函数
    	{
    		Insert(end(),x); //直接插入到最后一个元素的位置
    	}
    
void Insert(iterator pos, const T& x)   //在POS 的前面插入
    	{
    		assert(pos <= _finish);  //预插入的当前位置pos有效
    		size_t posindex = pos - _start;   //找到插入位置pos的下标
    		if (_finish == _endOfStorage)   //若当前vector中的元素已满,则按2倍扩容
    		{
    			size_t newCapacity = Capacity() == 0 ? 2 : Capacity() * 2;
    			Reserve(newCapacity);
    			pos = _start + posindex;  //重新找到需要插入位置pos的下标
    		}
    		iterator end = _finish; //当前尾指针赋值给迭代器
    		while (end>pos)  //从最后一个元素开始,依次向后移一位,直到移动pos位置的元素
    		{
    			*end = *(end - 1);
    			--end;
    		}
    		*pos = x;  //pos位置插入值x
    		++_finish;  //数组的尾指针向后移一位
    	}

8.删除函数

erase()删除操作对vector来说,不仅是效率很低的一件事情(与insert原因相同),还很容易使当前迭代器失效。所以我们在实现函数时,一定要注意保证当前迭代器的指针不为野指针。

 void PopBack()  //尾删
    		{
    			//--_finish;   //直接将尾指针向前移一位
    			Erase(--end());  //删除迭代器所指的最后一位元素
    		}
    iterator Erase(iterator pos)    //删除POS 位置
    		{
    			assert(pos < end());   //预删除的当前位置pos有效
    			iterator next = pos;  //保证迭代器指针有效
    			while (pos < _finish - 1)  //用后一位的元素依次覆盖前一个值
    			{
    				*pos = *(pos + 1);
    				++pos;
    			}
    			--_finish;  //尾指针向前移一位
    			return next;  //返回当前迭代器
    		}

9.下标运算符[] 重载

实现vector通过下标找到正确的值,因此具有高效的随机访问能力

   T& operator[](size_t pos)
    		{
    			assert(pos < Size());
    			return _start[pos];  //返回当前下标的值
    		}
    
    const T&operator[](size_t pos)const
    		{
    		assert(pos < Size());
    		return _start[pos];
    		}

附:完整代码实例

 #include <stdlib.h>
    #include <stdio.h>
    #include <assert.h>
    #include <iostream >
    #include <string.h>
    using namespace std;  
    
    namespace my_vector  //防止与库内的方法重名,使用自己的命名空间
    {
    	template<class T>  //定义类模板
    	
    	class Vector  
    	{
    	public:
    		typedef T* iterator;
    		typedef const T* const_iterator;
    		
    		Vector()       //无参构造函数
    			:_start(nullptr)
    			, _finish(nullptr)
    			, _endOfStorage(nullptr)
    		{}
    		//Vector() = default;
    		
    		Vector(const Vector<T>& v)     //拷贝构造函数
    		{
    			_start = new T[v.Capacity()];
    			//memcpy(_start, v._start, sizeof(T)*v.Size());   这里是浅拷贝
    			for (size_t i = 0; i < v.Size(); ++i)
    			{
    				_start[i] = v._start[i];
    			}
    			_finish = _start + v.Size();
    			_endOfStorage = _start + v.Capacity();
    		}
    		
    		Vector <T>& operator =(Vector <T> v)        //赋值重载函数
    		{
    			Swap(v);   //交换值和位置
    			return *this;
    		}
    		void Swap(Vector <T> &v)  
    		{
    			std::swap(_start, v._start);
    			std::swap(_finish, v._finish);
    			std::swap(_endOfStorage, v._endOfStorage);
    		}
    		
    		~Vector()   //析构函数
    		{
    			if (_start)
    			{
    				delete[] _start;
    				_start = _finish = _endOfStorage = nullptr;
    			}
    		}

            iterator begin() { return _start; }
    		iterator end()  { return _finish; }
    		const_iterator cbegin()const { return _start; }
    		const_iterator cend()const  { return _finish; }
    		size_t Size()   { return _finish - _start; }   //返回数组大小
    		size_t Capacity(){ return _endOfStorage - _start; }   //返回数组容量
    		size_t Size() const { return _finish - _start; }
		    size_t Capacity() const { return _endOfStorage - _start; }
		    
	   		void Reserve(size_t n)   //动态扩容--改变当前空间大小
    		{
    			if (n > Capacity())
    			{
    				size_t size = 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 + size;
    				_endOfStorage = _start + n;
    			}
    		}
    		void Resize(size_t n, const T& value = T())  //改变当前使用数据大小
    		{
    			if (n <= Size())
    			{
    				_finish = _start + n;
    				return;
    			}
    			else
    			{
    				if (n > Capacity())
    				{
    					Reserve(n);
    				}
    				while (_finish != _start + n)
    				{
    					*_finish = value;
    					++_finish;
    				}
    			}
    		}
    		
    	void PushBack(const T&x)  //在数组最后添加一个数
    	{
    		Insert(end(),x);
    	}
    	void Insert(iterator pos, const T& x)   //在pos 的前面插入一个数
    	{
    		assert(pos <= _finish);
    		size_t posindex = pos - _start;
    		if (_finish == _endOfStorage)
    		{
    			size_t newCapacity = Capacity() == 0 ? 2 : Capacity() * 2;
    			Reserve(newCapacity);
    			pos = _start + posindex;
    		}
    		iterator end = _finish;
    		while (end>pos)
    		{
    			*end = *(end - 1);
    			--end;
    		}
    		*pos = x;
    		++_finish;
    	}
    	
    	void PopBack()      //删除最后一个数
    		{
    			//--_finish;
    			Erase(--end());
    		}
    	iterator Erase(iterator pos)    //删除POS 位置的数
    		{
    			assert(pos < end());
    			iterator next = pos;
    			while (pos < _finish - 1)
    			{
    				*pos = *(pos + 1);
    				++pos;
    			}
    			--_finish;
    			return next;
    		}

    	T& operator[](size_t pos)  //重载下标运算符[]
    		{
    			assert(pos < Size());
    			return _start[pos];
    		}
    	const T&operator[](size_t pos)const
    		{
    		assert(pos < Size());
    		return _start[pos];
    		}
    		
    		private:   //成员函数
    		iterator _start = nullptr;           //指向数据块的开始
    		iterator _finish = nullptr;        //指向有效数据的结尾
    		iterator _endOfStorage = nullptr;  //指向储存容量的尾
    	};

附:测试示例

void Test1()     //测试函数
    	{
    		Vector<int> v1;  
    		v1.PushBack(1);
    		v1.PushBack(2);
    		v1.PushBack(3);
    		v1.PushBack(4);
    		cout << "尾插四个数:  ";
    		for (size_t i = 0; i < v1.Size(); i++)
    		{
    			cout << v1[i] << " ";
    		}
    		cout << endl;
    
    		v1.Resize(3);
    		Vector<int>::iterator it1 = v1.begin();
    		cout << "Resize(3):  ";
    		while (it1 != v1.end())
    		{
    			cout << *it1 << " ";
    			++it1;
    		}
    		cout << endl;
    
    		v1.Resize(10, 5);
    		cout << "Resize(10, 5):  ";
    		for (auto e : v1)
    		{
    			cout << e << " ";
    		}
    		cout << endl;
    	}
    	void Test2()
    	{
    		Vector<int> v1;
    		v1.PushBack(1);
    		v1.PushBack(2);
    		v1.PushBack(3);
    		v1.PushBack(4);
    		v1.PushBack(5);
    
    		auto pos = std::find(v1.begin(), v1.end(), 3);
    		if (pos != v1.end())
    		{
    			v1.Insert(pos, 30);
    		}
    		cout << "插入数据后: ";
    		for (size_t i = 0; i < v1.Size(); ++i)
    		{
    			cout << v1[i] << " ";
    		}
    		cout << endl;
    		pos = std::find(v1.begin(), v1.end(), 3);
    		v1.Erase(pos);
    		cout << "删除数据后: ";
    		for (size_t i = 0; i < v1.Size(); ++i)
    		{
    			cout << v1[i] << " ";
    		}
    		cout << endl;
    	}
    
    	void Test3()
    	{
    		Vector<int> v1;
    		v1.PushBack(1);
    		v1.PushBack(2);
    		v1.PushBack(3);
    		v1.PushBack(4);
    		v1.PushBack(5);
    
    		Vector<int> copy = v1;    // copy(v1)
    		for (auto e : copy)
    		{
    			cout << e << " ";
    		}
    		cout << endl;
    
    		Vector<int> v2;
    		v2.PushBack(10);
    		v2.PushBack(20);
    
    		copy = v2;
    		for (auto e : copy)
    		{
    			cout << e << " ";
    		}
    		cout << endl;
    	}
    
    	void Test4()
    	{
    		Vector<std::string> v;
    		v.PushBack("111");
    		v.PushBack("222");
    		v.PushBack("333");
    		v.PushBack("333");
    		v.PushBack("333");
    		v.PushBack("333");
    		v.PushBack("333");
    		v.PushBack("333");
    		v.PushBack("333");
    		v.PushBack("333");
    		v.PushBack("333");
    		for (auto e : v)
    		{
    			cout << e << " ";
    		}
    		cout << endl;
    	}
    }

  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值