C++从青铜到王者第十篇:STL之vector类的模拟实现

Posted 森明帮大于黑虎帮

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++从青铜到王者第十篇:STL之vector类的模拟实现相关的知识,希望对你有一定的参考价值。

在这里插入图片描述

系列文章目录



前言


在这里插入图片描述

一、vector深度剖析及模拟实现

在这里插入图片描述

在这里插入图片描述

1.vector的核心接口模拟实现

namespace yyw
{
	template<class T>
	class vector
	{
	public:
		typedef T* iterator;
		typedef const T* const_iterator;
	public:
		iterator begin()
		{
			return _start;
		}
		iterator end()
		{
			return _finish;
		}
		
		const_iterator begin() const
		{
			return _start;
		}
		const_iterator end() const
		{
			return _finish;
		}
		T& operator[](size_t i)
		{
			return _start[i];
		}

		//前面一个const不能写,只能读,后面的const 成员函数是因为const对象只能调用const成员函数
		//非const对象既可以调用非const成员函数也可以调用const成员函数
		const T& operator[](size_t i) const   
		{
			return _start[i];
		}

		size_t size() const
		{
			return _finish - _start;
		}
		size_t capacity() const
		{
			return _endofstorage - _start;
		}
		bool empty()
		{
			return (_finish - _start == 0);
		}

		void reserve(size_t n)
		{
			if (n > capacity())
			{
				size_t oldsize = size();                      //防止迭代器失效的问题
				T* tmp = new T[n];
				if (_start)
				{
					//memcpy(tmp, _start, sizeof(T)*size());   
					//memcpy,strcpy第一次拷贝的时候不能给空,memcpy不能使用进行对象之间的拷贝,按字节拷贝,浅拷贝
					for (size_t i = 0; i < oldsize; i++)
					{
						tmp[i] = _start[i];   //调用的是T将operator=的深拷贝
					}
					delete[] _start;
				}
				_start = tmp;
				//_finish = tmp + size();
				//_endofstorage = tmp + capacity();
				_finish = tmp + oldsize;
				_endofstorage = tmp + n;
			}
		}
		void resize(size_t n, const T& value = T())
		{
			if (n <= size())                    //n小于原来的size时候,数据缩小到n
			{
				_finish = _start + n;
				return;
			}
			else
			{
				if (n > capacity())
				{
					reserve(n);
				}  //memset不能对自定义类型初始化,得一个一个赋值
				iterator it = _finish;
				_finish = _start + n;
				while (it != _finish)
				{
					*it = value;
					it++;
				}
			}
		}

		void push_back(const T& x)
		{
			if (_finish == _endofstorage)
			{
				size_t newcapacity = capacity() == 0 ? 2:capacity() * 2;
				reserve(newcapacity);
			}
			*_finish = x;
			_finish++;
		}
		void pop_back()
		{
			assert(_start < _finish);
			_finish--;
		}
		iterator insert(iterator pos, const T& x)
		{
			assert(pos <= _finish);       //=_finish就是尾插
			if (_finish == _endofstorage)
			{
				size_t n = pos - _start; //防止迭代器失效问题

				size_t newcapacity = capacity() == 0 ? 2 : capacity() * 2;

				reserve(newcapacity);
				pos = _start + n;  //防止迭代器失效问题
			}
			iterator end = _finish - 1;
			while (end >= pos)
			{
				*(end + 1) = *end;
				end--;
			}
			*pos = x;
			_finish++;
			return pos;
		}
		iterator erase(iterator pos)
		{
			assert(pos < _finish);
			iterator it = pos;   
			while (it < _finish)  //最后是把it移动到_finish上面去了
			{
				*it = *(it + 1);
				it++;
			}
			_finish--;
			return pos;
		}



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

		~vector()
		{
			delete[] _start;
			_start = _finish = _endofstorage = nullptr;
		}
		//v2(v1)
		vector(const vector<T>& v)     //拷贝构造
		{
			_start = new T[v.capacity()];
			_finish = _start;
			_endofstorage = _start + v.capacity();
			for (size_t i = 0; i < v.size(); i++)
			{
				*_finish = v[i];
				_finish++;
			}
		}

		//v2(v1)先把v2初始化为空,遍历v1,把v1插入给v2;
		//vector(vector<T>& v)
		//	:_start(nullptr)
		//	, _finish(nullptr)
		//	, _endofstorage(nullptr)
		//{
		//	for (auto& e : v)
		//	{
		//		reserve(v.capacity());
		//		push_back(e);
		//	}
		//}
		
		//s2=s1;  //赋值运算符重载
		void Swap(vector<T>& v)
		{
			std::swap(_start, v._start);
			std::swap(_finish, v._finish);
			std::swap(_endofstorage, v._endofstorage);
		}

		//v3=v1;
		vector<T>& operator=(vector<T>& v)
		{
			//Swap(v);
			//return *this;
			if (this != &v)   //赋值运算符重载要检查是否自己给自己赋值
			{
				delete[] _start;
				_start = new T[v.capacity()];
				memcpy(_start, v._start, sizeof(vector<T>)*size());
			}
			return *this;
		}
	//private:
	//	T* _a;
	//	size_t size;
	//	size_t capacity;
	private:
		iterator _start;
		iterator _finish;
		iterator _endofstorage;
	};
}

其中遇到的问题:

  • resize开辟有效数据空间并初始化时不能使用memset初始化,因为mem系列函数是按字节去处理。
    在这里插入图片描述
  • reserve开辟容量空间时开辟的新空间不能使用memcpy把旧空间的数据拷贝过去,要使用循环赋值过去,因为memcpy是浅拷贝,浅拷贝最后会导致释放旧空间新空间的指针指向一块已被释放的空间,造成程序崩溃
    在这里插入图片描述

2.vector的核心接口测试

void testvector1()
	{
		vector<int> v1;
		v1.push_back(1);
		v1.push_back(2);
		v1.push_back(3);
		v1.push_back(4);
		v1.pop_back();
		vector<int>::iterator it = v1.begin();
		while (it != v1.end())
		{
			//*it += 1;
			std::cout << *it << " ";
			it++;
		}
		std::cout << std::endl;
		for (size_t i = 0; i < v1.size(); i++)
		{
			v1[i] -= 1;
			std::cout << v1[i] << " ";
		}
		std::cout << std::endl;
		for (auto& e : v1)          //支持迭代器就支持范围for
		{ 
			e -= 1;
			std::cout << e << " ";
		}
		std::cout << std::endl;
	}
	void testvector2()
	{
		const vector<int> v2;     //const对象的vector
		vector<int>::const_iterator vit = v2.begin();
		while (vit != v2.end())
		{
			//*vit += 1;
			std::cout << *vit << " ";
		}
		std::cout << std::endl;
	}
	void testvector3()
	{
		vector<int> v1;
		v1.push_back(1);
		v1.push_back(2);
		v1.push_back(3);
		v1.insert(v1.begin() + 1, 10);
		v1.erase(v1.begin() + 1);
		for (auto e : v1)
		{
			std::cout << e << " ";
		}
		std::cout << std::endl;
	}
	void testvector4()
	{
		vector<int> v1;
		v1.push_back(1);
		v1.push_back(2);
		v1.push_back(3);
		v1.push_back(4);
		v1.push_back(5);
		v1.push_back(6);

		std::cout << v1.size() << std::endl;
		std::cout << v1.capacity() << std::endl;
		v1.resize(10);
		v1.reserve(20);
		for (auto& e : v1)          //支持迭代器就支持范围for
		{
			std::cout << e << " ";
		}
		std::cout << std::endl;
		std::cout << v1.size() << std::endl;
		std::cout << v1.capacity() << std::endl;

		v1.resize(12);
		v1.reserve(30);
		for (auto& e : v1)          //支持迭代器就支持范围for
		{
			std::cout << e << " ";
		}
		std::cout << std::endl;
		std::cout << v1.size() << std::endl;
		std::cout << v1.capacity() << std::endl;
	}
	void testvector5()
	{
		vector<int> v1;
		v1.push_back(10);
		v1.push_back(9);
		v1.push_back(8);
		v1.push_back(7);
		v1.push_back(6);
		vector<int>v2(v1);
		for (auto& e : v1)
		{
			std::cout << e << " ";
		}
		std::cout << std::endl;
		for (auto& e : v2)
		{
			std::cout << e << " ";
		}
		std::cout << std::endl;
		vector<int> v3;
		v3 = v1;
		for (auto& e : v2)
		{
			std::cout << e << " ";
		}
		std::cout << std::endl;
	}

测试文件:

#define _CRT_SECURE_NO_WARNINGS   1
#include"vector.h"
int main()
{
	//yyw::testvector1();
	//yyw::testvector2();
	//yyw::testvector3();
	//yyw::testvector4();
	yyw::testvector5();
	return 0;
}

3.使用memcpy拷贝问题

假设模拟实现的vector中的reserve接口中,使用memcpy进行的拷贝,以下代码会发生什么问题?

int main()
{
	bite::vector<bite::string> v;
	v.push_back("1111");
	v.push_back("2222");
	v.push_back("3333");
	return 0;
}
  • 问题分析:
  • memcpy是内存的二进制格式拷贝,将一段内存空间中内容原封不动的拷贝到另外一段内存空间中。
    在这里插入图片描述
  • 如果拷贝的是自定义类型的元素,memcpy即高效又不会出错,但如果拷贝的是自定义类型元素,并且自定义类型元素中涉及到资源管理时,就会出错,因为memcpy的拷贝实际是浅拷贝。
    在这里插入图片描述
    在这里插入图片描述

4.动态二维数组理解


// 以杨慧三角的前n行为例:假设n为5
void test5(size_t n)
{
	// 使用vector定义二维数组vv,vv中的每个元素都是vector<int>
	yyw::vector<yyw::vector<int>> vv(n);
	// 将二维数组每一行中的vecotr<int>中的元素全部设置为1
	for (size_t i = 0; i < n; ++i)
		vv[i].resize(i + 1, 1);
	// 给杨慧三角出第一列和对角线的所有元素赋值
	for (int i = 2; i < n; ++i)
	{
		for (int j = 1; j < i; ++j)
		{
			vv[i][j] = vv[i - 1][j] + vv[i - 1][j - 1];
		}
	}
}

yyw::vector<yyw::vector> vv(n); 构造一个vv动态二维数组,vv中总共有n个元素,每个元素都是vector类型的,每行没有包含任何元素,如果n为5时如下所示:
在这里插入图片描述
vv中元素填充完之后:
在这里插入图片描述
使用标准库中vector构建动态二维数组时与上图实际是一致的。


总结

以上就是今天要讲的内容,本文介绍了vecror类的常见拷贝问题和模拟实现,而vector类提供了大量能使我们快速便捷地处理数据的函数和方法,非常的便捷,所以我们务必掌握。另外如果上述有任何问题,请懂哥指教,不过没关系,主要是自己能坚持,更希望有一起学习的同学可以帮我指正,但是如果可以请温柔一点跟我讲,爱与和平是永远的主题,爱各位了。
在这里插入图片描述

以上是关于C++从青铜到王者第十篇:STL之vector类的模拟实现的主要内容,如果未能解决你的问题,请参考以下文章

C++从青铜到王者第九篇:STL之vector类的初识

C++从青铜到王者第十一篇:STL之list类的初识

C++从青铜到王者第十四篇:STL之stack类的初识和模拟实现

C++从青铜到王者第十五篇:STL之queue类的初识和模拟实现

C++从青铜到王者第十六篇:STL之priority_queue类的初识和模拟实现

C++从青铜到王者第二十篇:STL之setmapmultisetmultimap的初识