19. C++STL 5(深入理解vector,vector的模拟实现,二维动态数组)

⭐本篇重点:vector深入理解和模拟实现

⭐本篇代码:c++学习/09.vector-2 · 橘子真甜/c++-learning-of-yzc - 码云 - 开源中国 (gitee.com)

目录

一. 深入理解vector

二. 使用模板模拟实现vector (包含迭代器)

2.1 模拟vector类的成员

2.1 size capacity

2.3 构造函数与拷贝构造函数

2.4 析构函数

2.5 reverse与resize

2.6 Insert与push_back 

2.7 测试代码1

​编辑 2.8 erase和pop_back

2.9 测试代码2

2.10 迭代器begin和end

2.11 测试代码3

三. 测试类类型的变量

四. vector完成动态二维数组 


一. 深入理解vector

        上篇vector我们提到,当vector插入元素之后,如果空间不足就会扩容1.5倍或者2倍。如果扩容之后空间仍然不够会再次扩容。

        并且vector的扩容不是在原空间上直接扩容,而是新开辟一份空间,拷贝原空间的数据到新空间,放入新元素,释放空间等过程。

        所以我们使用vector之前,要先预算并开辟要使用的空间。避免频繁扩容导致效率降低。最好不要频繁的使用push_back

二. 使用模板模拟实现vector (包含迭代器)

源文件 test.cpp用于测试

头文件 myVector.h用于声明和定义vector类

2.1 模拟vector类的成员

        根据我们使用的vector,可以知道vector至少有这些成员变量

#pragma once
#include <iostream>
using  namespace std;

namespace YZC
{
	template<class T>
	class myVector
	{
	public:

	private:
		T* _begin;	      //数组开始
		T* _end;	      //数组结尾
		T* _endOfStorage; //容量结尾
	};
}

_begin表示这个容器开始的位置,_end表示结尾位置。_endOfStorage表示容量结尾的位置

为了支持迭代器,我们可以进一步封装 

#pragma once
#include <iostream>
using  namespace std;

namespace YZC
{
	template<class T>
	class myVector
	{
		//添加迭代器
		typedef T* iterator;
		typedef const T* const_iterator;
	public:

	private:
		iterator _begin;	    //数组开始
		iterator _end;	        //数组结尾
		iterator _endOfStorage; //容量结尾
	};
}

 而成员函数包括:

        构造函数,析构函数,拷贝构造,赋值运算符重载,扩容reverse,设置元素resize,插入元素push_back,insert,删除元素pop_back,erase等...

        以及 size,capacity,begin,end,[]重载,等访问修改元素,容量,迭代器等函数

2.1 size capacity

        这两个函数比较简单,直接通过迭代器相减即可获得

		//获取容量
		size_t capacity()
		{
			return _endOfStorage - _begin;
		}

		//[]重载
		T& operator[](size_t i)
		{
			//注意要防止数组越界
			assert(i < size());
			return _begin[i];
		}

		//[]重载
		const T& operator[](size_t i)const
		{
			assert(i < size());
			return _begin[i];
		}

2.3 构造函数与拷贝构造函数

        主要拷贝构造函数需要进行深拷贝

#pragma once
#include <iostream>
using  namespace std;

namespace YZC
{
	template<class T>
	class myVector
	{
		//添加迭代器
		typedef T* iterator;
		typedef const T* const_iterator;
	public:
		//构造函数
		myVector()
			:_begin(nullptr)
			, _end(nullptr)
			, _endOfStorage(nullptr)
		{};

		//拷贝构造
		myVector(const myVector& v)
		{
			//需要深拷贝
			//依次拷贝成员变量,然后赋值
			_begin = new T[v.capacity()];
			_end = _begin;	//end在拷贝元素之后计算获取
			_endOfStorage = _begin + v.capacity();

			for (size_t i = 0; i < v.size(); i++)
			{
				*_end = v[i];
				_end++;
			}
		}

		//获取元素数量
		size_t size()
		{
			return _end - _begin;
		}

		//获取容量
		size_t capacity()
		{
			return _endOfStorage - _begin;
		}

		//[]重载
		T& operator[](size_t i)
		{
			//注意要防止数组越界
			assert(i < size());
			return _begin[i];
		}

		//[]重载
		const T& operator[](size_t i)const
		{
			assert(i < size());
			return _begin[i];
		}
	private:
		iterator _begin;	    //数组开始
		iterator _end;	        //数组结尾
		iterator _endOfStorage; //容量结尾
	};
}

2.4 析构函数


		~myVector()
		{
			delete[] _begin;
			_begin = _end = _endOfStorage = nullptr;
		}

2.5 reverse与resize

        注意好扩容的机制即可,开辟新空间,转移元素,销毁旧空间。

注意拷贝数据的时候,要深拷贝,不能简单的使用memcpy进行浅拷贝

		void reserve(size_t n)
		{
			//防止非法扩容
			assert(n >= 0);
			//开始扩容
			if (n > capacity())
			{
				//开辟新空间
				size_t oldsize = size();	//旧空间元素的大小
				T* newbegin = new T[n];
				if (_begin != nullptr)
				{
					//拷贝旧元素
					//不可以使用memcpy,因为这是浅拷贝
					for (size_t i = 0; i < oldsize; i++)
					{
						newbegin[i] = _begin[i];	//对于类类型,调用其operator= 是深拷贝
					}
					//删除旧空间
					delete[] _begin;
					_begin = nullptr;
				}
				_begin = newbegin;
				_end = _begin + oldsize;
				_endOfStorage = _begin + n;
			}
		}

		void resize(size_t n, const T& val = T())	//T()表示这种类型的默认值,比如int()=0
		{
			if (n <= size())
			{
				_end = _begin + n;
			}
			else
			{
				//如果n大于容量,调用reverse进行扩容
				if (n > capacity())
				{
					reserve(n);
				}
				//赋值val
				while (_end < _begin+ n)
				{
					*_end = val;
					_end++;
				}
			}
		}

2.6 Insert与push_back 

        对于push_back我们可以复用Insert的代码,直接在结尾插入。所以我们主要实现Insert这个函数的代码。

Insert

		//在pos位置插入元素x
		void Insert(iterator pos, const T& x)
		{
			//1. 需要合法插入
			assert(pos <= _end);	//当pos和end相等的时候就是尾插push_back
			//2. 判断是否需要扩容
			if (_end == _endOfStorage)
			{
				//扩容导致迭代器失效,需要提前计算好pos的位置
				size_t newpos = pos - _begin;
				size_t newcapacity = capacity() == 0 ? 3 : capacity() * 2;
				reserve(newcapacity);
				pos = _begin + newpos; //重新设置pos
			}

			//插入元素
			iterator end = _end - 1;
			while (end >= pos)    //不可end != pos    假如只有一个数据就会崩溃
			{
				*(end + 1) = *end;
				--end;
			}
			*pos = x;
			_end++;
		}

push_back

复用Insert的代码

		//尾插
		void push_back(const T& x)
		{
			Insert(_end, x);
		}

2.7 测试代码1

        我们的代码已经基本完善了,可以进行一次测试验证是否有bug

头文件 myVector.h 如下 

#pragma once
#include <iostream>
#include <cassert>
using  namespace std;

namespace YZC
{
	template<class T>
	class myVector
	{
		//添加迭代器
		typedef T* iterator;
		typedef const T* const_iterator;
	public:
		//构造函数
		myVector()
			:_begin(nullptr)
			, _end(nullptr)
			, _endOfStorage(nullptr)
		{};

		//拷贝构造
		myVector(const myVector& v)
		{
			//需要深拷贝
			//依次拷贝成员变量,然后赋值
			_begin = new T[v.capacity()];
			_end = _begin;	//end在拷贝元素之后计算获取
			_endOfStorage = _begin + v.capacity();

			for (size_t i = 0; i < v.size(); i++)
			{
				*_end = v[i];
				_end++;
			}
		}

		~myVector()
		{
			delete[] _begin;
			_begin = _end = _endOfStorage = nullptr;
		}

		void reserve(size_t n)
		{
			//防止非法扩容
			assert(n >= 0);
			//开始扩容
			if (n > capacity())
			{
				//开辟新空间
				size_t oldsize = size();	//旧空间元素的大小
				T* newbegin = new T[n];
				if (_begin != nullptr)
				{
					//拷贝旧元素
					//不可以使用memcpy,因为这是浅拷贝
					for (size_t i = 0; i < oldsize; i++)
					{
						newbegin[i] = _begin[i];	//对于类类型,调用其operator= 是深拷贝
					}
					//删除旧空间
					delete[] _begin;
					_begin = nullptr;
				}
				_begin = newbegin;
				_end = _begin + oldsize;
				_endOfStorage = _begin + n;
			}
		}

		void resize(size_t n, const T& val = T())	//T()表示这种类型的默认值,比如int()=0
		{
			if (n <= size())
			{
				_end = _begin + n;
			}
			else
			{
				//如果n大于容量,调用reverse进行扩容
				if (n > capacity())
				{
					reserve(n);
				}
				//赋值val
				while (_end < _begin+ n)
				{
					*_end = val;
					_end++;
				}
			}
		}

		//在pos位置插入元素x
		void Insert(iterator pos, const T& x)
		{
			//1. 需要合法插入
			assert(pos <= _end);	//当pos和end相等的时候就是尾插push_back
			//2. 判断是否需要扩容
			if (_end == _endOfStorage)
			{
				//扩容导致迭代器失效,需要提前计算好pos的位置
				size_t newpos = pos - _begin;
				size_t newcapacity = capacity() == 0 ? 3 : capacity() * 2;
				reserve(newcapacity);
				pos = _begin + newpos; //重新设置pos
			}

			//插入元素
			iterator end = _end - 1;
			while (end >= pos)
			{
				*(end + 1) = *end;
				--end;
			}
			*pos = x;
			_end++;
		}

		//尾插
		void push_back(const T& x)
		{
			Insert(_end, x);
		}

		//获取元素数量
		size_t size()
		{
			return _end - _begin;
		}

		//获取容量
		size_t capacity()
		{
			return _endOfStorage - _begin;
		}

		//[]重载
		T& operator[](size_t i)
		{
			//注意要防止数组越界
			assert(i < size());
			return _begin[i];
		}

		//[]重载
		const T& operator[](size_t i)const
		{
			assert(i < size());
			return _begin[i];
		}
	private:
		iterator _begin;	    //数组开始
		iterator _end;	        //数组结尾
		iterator _endOfStorage; //容量结尾
	};
}

源文件 test.cpp

#define _CRT_SECURE_NO_WARNINGS 1
#include "myVector.h"

void test1()
{
	YZC::myVector<int> arr;
	//插入20个随机值
	for (int i = 0; i < 20; i++)
	{
		arr.push_back(rand());
	}

	//遍历输出
	for (int i = 0; i < arr.size(); i++)
	{
		cout << arr[i] << " ";
	}
	cout << endl;
	//使用[]修改
	for (int i = 0; i < 20; i++)
	{
		arr[i] = i;
	}

	for (int i = 0; i < arr.size(); i++)
	{
		cout << arr[i] << " ";
	}
	cout << endl;
}

int main()
{
	//设置随机种子
	srand((unsigned int)time(0));
	test1();
	return 0;
}

运行结果如下:

 
2.8 erase和pop_back

        erase将pos后面的元素一个一个向前移动即可,最后让_end减去1

        而pop_back只要让_end减去1即可

		iterator& Erase(iterator pos)
		{
			assert(pos < _end);
			iterator it = pos;
			while (it != _end)
			{
				*it = *(it + 1);
				it++;
			}
			_end--;
			return pos;
		}

		void pop_back()
		{
			assert(_begin < _end);
			--_end;
		}

2.9 测试代码2

#define _CRT_SECURE_NO_WARNINGS 1
#include "myVector.h"

void test1()
{
	YZC::myVector<int> arr;
	for (int i = 0; i < 20; i++)
	{
		arr.push_back(i);
	}

	arr.pop_back();
	arr.pop_back();
	arr.pop_back();
	arr.push_back(1234);

	for (int i = 0; i < arr.size(); i++)
	{
		cout << arr[i] << " ";
	}
	cout << endl;
}

int main()
{
	test1();
	return 0;
}

 

2.10 迭代器begin和end

		//开始位置的迭代器
		iterator begin()const
		{
			return _begin;
		}

		//结束位置后一个的迭代器
		iterator end()const
		{
			return _end;
		}

 有了begin和end后,我们就可以使用迭代器遍历和C++11 for循环

2.11 测试代码3

使用迭代器注意要将iterator设置为public

测试代码:

#define _CRT_SECURE_NO_WARNINGS 1
#include "myVector.h"
#include <algorithm>
void test1()
{
	YZC::myVector<int> arr;
	//插入20个随机值
	for (int i = 0; i < 20; i++)
	{
		arr.push_back(rand() % 1000);
	}
	//1. 迭代器遍历
	YZC::myVector<int>::iterator it = arr.begin();
	while (it != arr.end())
	{
		cout << *it << " ";
		it++;
	}
	cout << endl;

	//使用sort和迭代器进行排序
	sort(arr.begin(), arr.end());	
	//2. C++11for循环
	for (const auto& e : arr)
		cout << e << " ";
	cout << endl;
}

int main()
{
	//设置随机种子
	srand((unsigned int)time(0));
	test1();
	return 0;
}

 运行结果如下:

三. 测试类类型的变量

        上面的代码,经过测试,基本数据类型是没什么问题的。那么对于自定义类型或者系统自带的类型有没有错误呢?

        我们以string类为测试

#define _CRT_SECURE_NO_WARNINGS 1
#include "myVector.h"
#include <string>

//测试string类型
void test2()
{
	YZC::myVector<string> arr;
	//插入元素
	string s1 = "YZC";
	string s2 = "123";
	string s3 = "vector";
	string s4 = "string";
	string s5 = "abc";
	string s6 = "231141234";

	arr.push_back(s1);
	arr.push_back(s2);
	arr.push_back(s3);
	arr.push_back(s4);
	arr.push_back(s5);
	arr.push_back(s6);

	for (const auto& e : arr)
		cout << e << " ";
	cout << endl;

	//删除和修改元素
	arr[0] = "yzc";
	arr.pop_back();
	for (const auto& e : arr)
		cout << e << " ";
	cout << endl;
}

int main()
{
	test2();
	return 0;
}

测试结果如下: 

        注意:如果是自定义类型,必须要定义operator=之后才能插入数据,否则会报错!

四. vector完成动态二维数组 

        vector可以完成一个动态一维数组,那么如果使用vector定义一个二维数组?其实很简单,只要在将一个vector作为vector的元素不就可以了?

举例如下:

#define _CRT_SECURE_NO_WARNINGS 1
#include "myVector.h"
#include <vector>

void test1()
{
	//vector中的元素是vector<int> 可以表示一个int的二维数组
	vector<vector<int>> arr;

	int index = 1;
	for (int i = 0; i < 6; i++)
	{
		vector<int> tmp;
		for (int j = 0; j < 6; j++)
		{
			tmp.push_back(index++);
		}
		arr.push_back(tmp);
	}

	for (int i = 0; i < 6; i++)
	{
		for (int j = 0; j < 6; j++)
		{
			cout << arr[i][j] << " ";
		}
		cout << endl;
	}

}

int main()
{
	test1();
	return 0;
}

运行结果:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

橘子真甜~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值