[C++入门]---vector深度剖析及模拟实现

news2024/12/27 15:19:11

文章目录

  • 1. vector功能函数模拟实现
    • vector成员变量定义
    • vecor构造函数
    • vector迭代器
    • vector的size函数
    • vector的capacity函数
    • vector的operator[]函数
    • vector的reserve函数
    • vector的resize函数
    • vector的insert函数
    • vector的erase函数
    • vector的push_back函数
    • vector的pop_back函数
    • vector的拷贝构造函数
    • vector的operator=运算符重载函数
    • vector的析构函数
  • 2.vector.h文件

1. vector功能函数模拟实现

vector成员变量定义

template<class T>
class vector
{
typedef T* iterator;
typedef const T* const_iterator;
private:
	iterator _start = nullptr;//指向vector第一个数据的位置
	iterator _finish = nullptr;//指向vector最后一个数据的下一个位置
	iterator _endofstorage = nullptr;//记录最大存储容量的下一个位置
}

template<class T>使用类模板,来支持vector类实现不同类型的数组;②通过typedef将对应类型的源生指针实现成vector的迭代器iterator;③自定义类型的成员变量会调用默认构造函数进行初始化,而内置类型的成员变量,不同的编译器实现方式不同,默认内置类型的成员变量为随机值;④C++11中针对内置类型成员不初始化的缺陷打了补丁,即内置类型成员变量在类中声明时可以给默认值;⑤为了避免内置类型成员为随机值带来的错误,给声明成员变量赋值为nullptr
在这里插入图片描述

vecor构造函数

//构造函数
vector()
	:_start(nullptr)
	, _finish(nullptr)
	, _endofstorage(nullptr)
{}

构造函数中使用初始化列表进行初始化

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;
}

begin()获取第一个数据位置的iteratorend()获取最后一个数据下一个位置的iterator,分别针对普通vector对象、const vector对象事项不同的begin()\end()函数。在这里插入图片描述

vector的size函数

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

[_start,_finish)为左闭右开区间,_finish - _startvector有效数据个数。

vector的capacity函数

size_t capacity()const
{
	return _endofstorage - _start;
}

[_start,_endofstorage)为左闭右开区间,_endofstorage - _startvector的存储容量。

vector的operator[]函数

T& operator[](size_t pos)
{
	assert(pos < size());
	return _start[pos];
}
const T& operator[](size_t pos)const
{
	assert(pos < size());
	return _start[pos];
}

针对不同对象实现不同版本的operator[]函数,判断pos位置是否在有效数据范围内。
在这里插入图片描述

vector的reserve函数

void reserve(size_t n)
{
	if (n > capacity())
	{
		size_t sz = size();
		T* tmp = new T[n];
		if (_start)
		{
			//memcpy对自定义类型进行的为浅拷贝
			//memcpy(tmp, _start, sizeof(T) * sz);
			//扩容的时候带来的,vector<自定义类型>带来的深拷贝问题
			for (int i = 0; i < size(); i++)
			{
				tmp[i] = _start[i];
			}
			delete[] _start;
		}
		_start = tmp;
		_finish = _start + sz;
		_endofstorage = _start + n;
	}
}

①使用if语句判断是否需要扩容,避免了缩容带来的效率降低;②使用new开辟n个T类型的空间;③如果vextor不是首次扩容,则需要拷贝数据,使用memcpy函数对内置类型数据进行浅拷贝效率很高,但是对自定义数据进行浅拷贝,会出现析构两次、空间通用的错误,需要使用for循环进行一对一赋值,若数据为内置类型,进行正常的赋值,若数据为自定义类型,则会调用该自定义类型的赋值运算符重载函数进行深拷贝,delete[] _start;可以释放vector空间⑤最后使用tmp\sz\n对扩容的地址_start\_finish\_endofstorage进行更新。
在这里插入图片描述

vector的resize函数

//调整vector的size()
void resize(size_t n, const T& val = T())
{
	if (n < size())
	{
		_finish = _start + n;
	}
	else
	{
		reserve(n);
		while (_finish != _start + n)
		{
			*_finish = val;
			_finish++;
		}
	}
}

使用if语句判断,n小于vector原有数据个数,直接对_finish位置进行更改;n大于vector原有数据个数,进行扩容后再进行赋值拷贝!

vector的insert函数

iterator insert(iterator pos, const T& x)
{
	assert(pos >= _start && pos <= _finish);
	if (_finish == _endofstorage)
	{
		//记录pos位置与_start位置的距离
		int len = pos - _start;
		size_t newcapacity = capacity() == 0 ? 3 : capacity() * 2;
		reserve(newcapacity);
		//解决迭代器失效问题
		pos = _start + len;
	}

	T* end = _finish - 1;
	while (end >= pos)
	{
		*(end + 1) = *end;
		end--;
	}
	*pos = x;//自定义类型会调用赋值运算符重载函数
	_finish++;
	return pos;
}

①使用assert判断pos位置是否和合法;②判断是否需要扩容,如果需要扩容,则需要记录pos位置与_start位置的距离,重新扩容后,_start指向新的空间,pos位置还指向已经释放掉的原有空间的位置,需要更新内部pos位置,解决内部pos迭代器失效问题;③挪动插入数据,返回pos的迭代器指向。

eg:

void Testinsert()
{
	vector<int> v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);
	v.push_back(5);
	for (auto e : v)
	{
		cout << e << " ";
	}
	cout << endl;
	//在insert外部使用pos记录位置
	vector<int>::iterator pos = v.begin();
	//使用pos位置进行插入
	v.insert(pos, 100);
	//再次对pos位置的数据进行操作
	*pos += 10;
	for (auto e : v)
	{
		cout << e << " ";
	}
	cout << endl;
}

VS结构下代码编译运行的结果为:
在这里插入图片描述
g++结构下编译的结果为:
在这里插入图片描述
总结:

①使用insert函数在pos位置迭代器插入数据,继续访问pos位置的数据,如果扩容这个pos位置的迭代器失效了;②在VS编译器上直接报错,在g++上正常运行;③如果要继续访问pos位置的数据,需要接收insert函数返回迭代器(即给it重新赋值即可)。

vector的erase函数

//任意删除
iterator erase(iterator pos)
{
	assert(pos >= _start && pos < _finish);
	iterator it = pos + 1;
	while (it != _finish)
	{
		*(it - 1) = *it;
		it++;
	}
	_finish--;
	return pos;
}

①判断删除的pos位置是否合法;②使用it记录pos下一个位置,然后挪动数据覆盖进行删除数据;③返回删除位置的迭代器。

eg:

void Testerase()
{
	vector<int> v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);
	v.push_back(5);
	v.push_back(6);
	for (auto e : v)
	{
		cout << e << " ";
	}
	cout << endl;
	vector<int>::iterator it = v.begin();
	while (it != v.end())
	{
		if (*it % 2 == 0)
		{
			v.erase(it);
		}
		else
		{
			it++;
		}
	}
	for (auto e : v)
	{
		cout << e << " ";
	}
}

VS结构下代码编译运行的结果为:
在这里插入图片描述

g++结构下编译的结果为:
在这里插入图片描述
总结:

①使用erase函数在pos位置迭代器删除数据,需要继续访问pos位置的数据,如果删除的pos位置的数据为最后一个数据,删除后该迭代器不属于有效范围,即迭代器失效了;②在VS编译器上直接报错,在g++上一般会正常运行;③如果要继续访问pos位置的数据,需要接收erase函数返回迭代器。(即给it重新赋值即可)。

vector的push_back函数

void push_back(const T& x)
{
	//方法一:
	//判断是否需要扩容
	if (_finish == _endofstorage)
	{
		size_t newcapacity = capacity() == 0 ? 3 : capacity() * 2;
		reserve(newcapacity);
	}
	*_finish = x;//自定义类型会调用赋值运算符重载函数
	_finish++;
}
void push_back(const T& x)
{
	//方法二:
	insert(end(), x);
}

使用if语句进行判断是否需要扩容,直接在_finish插入数据,更新_finish位置。

vector的pop_back函数

void pop_back()
{
	erase(end()-1);
}

使用erase函数进行尾删

vector的拷贝构造函数

方法一:

vector(const vector<T>& v)
{
	_start = new T[v.size()];
	//memcpy(_start, v._start, sizeof(T) * sz);
	for (int i = 0; i < v.size(); i++)
	{
		_start[i] = v._start[i];
	}
	_finish = _start + v.size();
	_endofstorage = _start + v.capacity();
}

方法二:

vector(const vector<T>& v)
{
	reserve(v.size());
	for (auto e : v)
	{
		push_back(e);
	}
}

拷贝构造函数方法一:开辟vector拷贝构造对象的相同空间,如果vector存储的数据类型为自定义类型,使用到memcpy函数会浅拷贝,需要使用for循环进行赋值,如果vector存储的数据类型为内置类型,则为正常的赋值;如果vector存储的数据类型为自定义类型,则会调用赋值运算符重载函数进行深拷贝。拷贝构造函数方法二:使用reserve函数开辟空间,使用范围forvector尾插函数push_back进行尾插vector拷贝构造对象的数据。

//使用n个val值初始化数组
vector(size_t n, const T& val = T())
{
	resize(n, val);
}
//针对vector<int,int>实现
vector(int n, const T& val = T())
{
	resize(n, val);
}

vector拷贝构造函数的形参const T& val = T()T()如果T类型为内置类型则val为编译器使用默认值;如果T类型为自定义类型,则会调用默认构造函数。

//使用vector的迭代区间进行初始化--左闭右开
//template<class IputIterator>
template<class InputIterator>
vector(InputIterator first, InputIterator last)
{
	while (first != last)
	{
		push_back(*first);
		first++;
	}
}

使用push_back函数插入拷贝构造对象迭代器区间的数据。

vector的operator=运算符重载函数

void swap(vector<T>& v)
{
	std::swap(_start, v._start);
	std::swap(_finish, v._finish);
	std::swap(_endofstorage, v._endofstorage);
}
vector<T>& operator=(vector<T> v)
{
	swap(v);
	return *this;
}

在运算符重载函数的形参会调用拷贝构造函数构造一个vector对象v,使用swap函数交换两个vector对象私有成员变量后,vector对象v指向旧空间(this原指向的空间),在operator=函数结束时会主动释放。

vector的析构函数

~vector()
{
	if (_start)
	{
		delete[] _start;
		_start = _finish = _endofstorage = nullptr;
	}
}

析构函数释放_start指向的空间,将_start\_finish\_endofstorage进行置空。

2.vector.h文件

#include<assert.h>
#include<iostream>
#include<vector>
#include<string>
using namespace std;
namespace hhl
{
	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)
		{}

		//拷贝构造 v1(v2)
		//vector(const vector<T>& v)
		//{
		//	_start = new T[v.size()];
		//	//memcpy(_start, v._start, sizeof(T) * sz);
		//	for (int i = 0; i < v.size(); i++)
		//	{
		//		_start[i] = v._start[i];
		//	}
		//	_finish = _start + v.size();
		//	_endofstorage = _start + v.capacity();
		//}
		vector(const vector<T>& v)
		{
			reserve(v.size());
			for (auto e : v)
			{
				push_back(e);
			}
		}
		//使用n个值初始化数组
		vector(size_t n, const T& val = T())
		{
			resize(n, val);
		}
		vector(int n, const T& val = T())
		{
			resize(n, val);
		}
		//使用vector的迭代区间进行初始化--左闭右开
		//template<class IputIterator>
		//vector(IputIterator first, IputIterator last)
		//{
		//	size_t n = last - first;
		//	_start = new T[n];
		//	int i = 0;
		//	while (first != last)
		//	{
		//		_start[i] = *first;
		//		i++;
		//		first++;
		//	}
		//	_finish = _start + n;
		//}
		// [first, last)
		template<class InputIterator>
		vector(InputIterator first, InputIterator last)
		{
			while (first != last)
			{
				push_back(*first);
				first++;
			}
		}				
		void swap(vector<T>& v)
		{
			std::swap(_start, v._start);
			std::swap(_finish, v._finish);
			std::swap(_endofstorage, v._endofstorage);
		}
		vector<T>& operator=(vector<T> v)
		{
			swap(v);
			return *this;
		}
		~vector()
		{
			if (_start)
			{
				delete[] _start;
				_start = _finish = _endofstorage = nullptr;
			}
		}
		void reserve(size_t n)
		{
			if (n > capacity())
			{
				size_t sz = size();
				T* tmp = new T[n];
				if (_start)
				{
					//memcpy(tmp, _start, sizeof(T) * sz);
					//扩容的时候带来的,vector<自定义类型>带来的深拷贝问题
					for (int i = 0; i < size(); i++)
					{
						tmp[i] = _start[i];
					}
					delete[] _start;
				}
				_start = tmp;
				_finish = _start + sz;
				_endofstorage = _start + n;
			}
		}
		void resize(size_t n, const T& val = T())
		{
			if (n < size())
			{
				_finish = _start + n;
			}
			else
			{
				reserve(n);
				while (_finish != _start + n)
				{
					*_finish = val;
					_finish++;
				}
			}
		}
		void push_back(const T& x)
		{
			//方法一:
			//判断是否需要扩容
			if (_finish == _endofstorage)
			{
				size_t newcapacity = capacity() == 0 ? 3 : capacity() * 2;
				reserve(newcapacity);
			}
			*_finish = x;
			_finish++;
			//方法二:
			insert(end(), x);
		}
		//复用erase函数
		void pop_back()
		{
			erase(end()-1);
		}
		//void insert(iterator pos,const T& x)
		iterator insert(iterator pos, const T& x)
		{
			assert(pos >= _start && pos <= _finish);
			if (_finish == _endofstorage)
			{
				//记录pos位置与_start位置的距离
				int len = pos - _start;
				size_t newcapacity = capacity() == 0 ? 3 : capacity() * 2;
				reserve(newcapacity);
				//解决迭代器失效问题
				pos = _start + len;
			}

			T* end = _finish - 1;
			while (end >= pos)
			{
				*(end + 1) = *end;
				end--;
			}
			*pos = x;
			_finish++;
			return pos;
		}
		//任意删除
		iterator erase(iterator pos)
		{
			//assert(pos >= _start && pos < _finish);
			iterator it = pos + 1;
			while (it != _finish)
			//while (it <= _finish)
			{
				*(it - 1) = *it;
				it++;
			}
			_finish--;
			return pos;
		}
		size_t size()const
		{
			return _finish - _start;
		}
		size_t capacity()const
		{
			return _endofstorage - _start;
		}
		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;
	};
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/920248.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

三维重建_体素重建_空间雕刻法/体素着色法

目录 1. 三角化和体素重建的区别 2. 空间雕刻法 空间雕刻法的一致性定义 空间雕刻法具体实现 基于八叉树的空间雕刻法具体实现​编辑 空间雕刻法效果展示 3. 体素着色法 体素着色法的缺点&#xff1a;不唯一性​编辑 体素着色法不唯一性解决措施​编辑 体素着色发实验环境与…

edge浏览器使用jupyter notebook删除快捷键没有用?

按快捷键删除没有用&#xff0c;出现一个黑色方框&#xff0c;里面的数字不断在加 解决方法&#xff1a; 在扩展中将Global Speed控制视频速度的插件关掉&#xff0c;或者将控制速度的快捷键改一下 可以在浏览器设置 》扩展 》管理扩展 里面关掉该插件 可以在Global Speed 的…

安达发APS|三分钟了解智能制造!

智能是由“智慧”和“能力”两个词语构成。从感觉到记忆到思维这一过程&#xff0c;称为“智慧”&#xff0c;智慧的结果产生了行为和语言&#xff0c;将行为和语言的表达过程称为“能力”&#xff0c;两者合称为“智能”。 因此&#xff0c;将感觉、记忆、回忆、思维、语言、…

1631. 最小体力消耗路径

你准备参加一场远足活动。给你一个二维 rows x columns 的地图 heights &#xff0c;其中 heights[row][col] 表示格子 (row, col) 的高度。一开始你在最左上角的格子 (0, 0) &#xff0c;且你希望去最右下角的格子 (rows-1, columns-1) &#xff08;注意下标从 0 开始编号&…

Python+TinyPNG熊猫网站自动化的压缩图片

前言 本篇在讲什么 PythonTinyPNG自动化处理图片 本篇需要什么 对Python语法有简单认知 依赖Python2.7环境 依赖TinyPNG工具 本篇的特色 具有全流程的图文教学 重实践&#xff0c;轻理论&#xff0c;快速上手 提供全流程的源码内容 ★提高阅读体验★ &#x1f449;…

VMware Workstation Pro 无法使用开机状态下拍的快照来克隆虚拟机,怎么解决?

环境: VMware Workstation Pro16.0 Win10 专业版 问题描述: VMware Workstation Pro有台虚拟机在开机状态下拍了个6.7快照这个win10初始版,现在想在这个快照下直接克隆,无法使用开机状态下拍的快照创建克隆 解决方案: 1.关闭当前虚拟机 2.到虚拟机文件夹复制一份Wind…

web---Vue2_语法学习

文章目录 1、Vue2 常用指令1.1 初始Vue1.2 Vue的两种模板语法1.3 数据绑定1.4 el和data的两种写法1.5 MVVM模型1.6 Vue中的数据代理1.7 事件处理--事件的基本使用1.7 事件处理--事件修饰符1.7 事件处理--键盘事件1.8 计算属性1.9 监视属性1.9 深度监视2.0 绑定css样式2.1 条件渲…

如果将PC电脑变成web服务器:使用python3监测公网IP实现DDNS

如果将PC电脑变成web服务器&#xff1a;使用python3监测公网IP实现DDNS 上一篇文章中&#xff0c;我们使用Nignx的反向代理和端口转发实现域名访问家里主机上的web了。 由于家庭宽带基本都是动态IP&#xff0c;每当你重启一次光猫&#xff0c;IP地址就会变化一次。当光猫因为…

网络知识点之-堆叠与集群(2-集群)

本文章收录至《网络》专栏&#xff0c;点击右上角专栏图标可访问本专栏&#xff01; 集群是一种用于集团调度指挥通信的​​​​​​移动通信系统&#xff0c;主要应用在专业移动通信领域。该系统具有的可用信道可为系统的全体用户共用&#xff0c;具有自动选择信道功能&#x…

5G NR:PRACH频域资源

PRACH在频域位置由IE RACH-ConfigGeneric中参数msg1-FrequencyStart和msg1-FDM所指示&#xff0c;其中&#xff0c; msg1-FrequencyStart确定PRACH occasion 0的RB其实位置相对于上行公共BWP的频域其实位置(即BWP 0)的偏移&#xff0c;即确定PRACH的频域起始位置msg1-FDM的取值…

UNIX网络编程卷一 学习笔记 第二十八章 原始套接字

原始套接字提供普通的TCP和UDP套接字不具备的以下3个能力&#xff1a; 1.有了原始套接字&#xff0c;进程可以读写ICMPv4、IGMPv4、ICMPv6等分组。例如&#xff0c;ping程序就使用原始套接字发送ICMP回射请求并接收ICMP回射应答。多播路由守护程序mrouted也使用原始套接字发送和…

Linux服务——http协议及nginx服务

目录 一、HTTP协议 1、跨网络的主机间通讯方式 套接字相关的系统调用 2、HTTP协议访问网站的过程 3、http协议状态码分类 常见的http协议状态码 4、MIME 5、URL组成 6、HTTP协议版本 7、系统处理http请求的工作模式 8、apache与nginx的区别 二、I/O模型 I/O模型相关…

春秋云镜 CVE-2019-16113

春秋云镜 CVE-2019-16113 Bludit目录穿越漏洞 靶标介绍 在Bludit<3.9.2的版本中&#xff0c;攻击者可以通过定制uuid值将文件上传到指定的路径&#xff0c;然后通过bl-kernel/ajax/upload-images.php远程执行任意代码。 启动场景 漏洞利用 exp https://github.com/Kenun…

Matlab绘制灰度图像

灰度图介绍 灰度图像是指每个像素的信息由一个量化的灰度级来描述的图像。如果每个像素的灰度值用一个字节表示&#xff0c;灰度值级数就等于256级&#xff0c;每个像素可以是0-255之间的任何一个数。其特点是&#xff1a;它只有亮度信息&#xff0c;没有颜色信息。占据存储空…

Java IO流(四)Netty理论[模型|核心组件]

概述 Netty是由JBOSS提供的一个Java开源框架,可从Github获取独立项目Netty是一个异步的、基于事件驱动的网络应用框架,用于快速开发可维护、高性能的网络服务器和客户端(摘录官网)Netty所谓的异步是针对用户使用Channel进行IO操作,会立即返回ChannelFuture。但IO操作的任务是提…

AutoSAR配置与实践(基础篇)3.5 BSW 的模式管理

传送门 -> AUTOSAR配置与实践总目录 AutoSAR配置与实践(基础篇)3.5 BSW 的模式管理 一、模式管理的组成二、模式项内容简介一、模式管理的组成 AUTOSAR为ECU的运行时软件的状态处理提供了模式管理组件,如下 • BswM模式管理器 • NM网络管理 • EcuM状态管理器 • ComM通…

Kaggle分类问题Titanic——Machine Learning from Disaster

目录 前言1 题目介绍2 数据清洗3 数据可视化分析4 模型训练5 源码 前言 这是我在大三选修课的课程设计&#xff0c;内容参考了Kaggle上高赞的代码&#xff0c;有详细批注&#xff0c;整体比较基础&#xff0c;结构相对完整&#xff0c;便于初学者学习。这个是一个分类问题&am…

vue 简单实验 v-if 条件判定

1.代码 <script src"https://unpkg.com/vuenext" rel"external nofollow" ></script> <div id"conditional-rendering"><span v-if"seen">现在你看到我了</span> </div> <script> const C…

纷享销客入选“寻找创新的「踏脚石」|36氪数字创新标杆案例”

近日&#xff0c;36氪重磅发布“数字创新标杆案例&先进团队名册”&#xff0c;本期围绕寻找创新的「踏脚石」的主题&#xff0c;共遴选出36个数字创新标杆案例与10个先进团队&#xff0c;纷享销客连接型CRM凭借过硬的产品及自主科研创新实力和服务实践有幸入选。 此次评选历…

分布式定时任务框架Quartz总结和实践(2)—持久化到Mysql数据库

本文主要介绍分布式定时任务框架Quartz集成SpringBoot持久化数据到Mysql数据库的操作&#xff0c;上一篇文章使用Quartz创建定时任务都是保存在内存中&#xff0c;如果服务重启定时任务就会失效&#xff0c;所以Quartz官方也提供将定时任务等信息持久化到Mysql数据库的功能&…