vector使用+模拟实现

news2024/9/28 5:35:00

目录

vector介绍

常见接口

构造函数

迭代器

容量操作

元素访问 

增删查改

模拟实现

模拟实现要点图解

 整体代码

迭代器失效问题

内部失效

外部失效

深浅拷贝问题


vector介绍

vector是表示可变大小数组的序列式容器。vector采用连续的空间存储元素,大小通过动态增长的方式改变,元素的访问比较高效

常见接口

构造函数

//构造函数测试
void Vector_Test1()
{
	//无参构造
	vector<int> v1;

	//初始化n个val构造
	vector<int> v2(5, 10);

	//拷贝构造
	vector<int> v3(v2);

	//迭代器区间构造
	vector<int> v4(v3.begin(), v3.end());
}

迭代器

 如上图所示,正向迭代器的begin指向首元素的迭代器位置,end指向末尾元素的下一个位置,【左闭,右开)。反向迭代器正好相反。

//迭代器测试
void Vector_Test2()
{
	vector<int> vv={ 1,2,3,4,5,6,7,8,9 };

	//vector<int>::iterator it = vv.begin();
	auto it = vv.begin();
	cout << "正向迭代:>" << endl;
	while (it != vv.end())
	{
		cout << *it << " ";
		it++;
	}
	cout << endl;

	auto rit = vv.rbegin();
	cout << "反向迭代:>" << endl;
	while (rit != vv.rend())
	{
		cout << *rit << " ";
		rit++;
	}
	cout << endl;

}

容量操作

简单接口测试:

//容量测试
void Vector_Test3()
{
	vector<int> vv(10,985);

	cout <<"size:>" << vv.size() << endl;
	cout << "capacity:>" << vv.capacity() << endl;
	cout << "empty? :>" << vv.empty() << endl;
}

 resize和reserve分析:

resize和reserve都有的共同点就是不会进行缩容,给我空间可以,想缩容,没门!

resize:

void Printf(vector<int> vv)
{
	auto it = vv.begin();
	while (it != vv.end())
	{
		cout << *it << " ";
		it++;
	}
	cout << endl;
}
void Vector_Test4()
{
	vector<int> vv = { 1,2,3,4,5,6,7,8,9 };
	
	vv.resize(15,1);
	cout << "newsize>size:" << endl;
	cout << "size:" << vv.size() << endl;
	cout << "capacity:" << vv.capacity() << endl;
	Printf(vv);


	vv.reserve(20);
	vv.resize(20, 100);
	cout << "newsize在容量范围内:" << endl;

	cout << "size:" << vv.size() << endl;
	cout << "capacity:" << vv.capacity() << endl;
	Printf(vv);



	vv.resize(5);
	cout << "newsize<size:" << endl;
	cout << "size:" << vv.size()<<endl;
	cout << "capacity:" << vv.capacity() << endl;

	Printf(vv);
}

reserve:新容量大扩容,新容量下不变!

void Vector_Test5()
{
	vector<int> vv = {1,2,3,4,5};
	cout << "----------容量增加,扩容-----------" << endl;
	cout << "capacity:" << vv.capacity() << endl;
	vv.reserve(10);
	cout << "newcapacity:" << vv.capacity() << endl;

	cout << "----------容量减少,不变-----------" << endl;
	cout << "capacity:" << vv.capacity() << endl;
	vv.reserve(5);
	cout << "newcapacity:" << vv.capacity() << endl;
}

默认扩容机制测试: vs下测试,默认按照1.5倍扩容!

void TestVectorExpand()
{
	size_t sz;
	vector<int> v;
	//记录每次扩容后的容量大小
	sz = v.capacity();
	int cnt = 1;
	for (int i = 0; i < 100; ++i)
	{
		v.push_back(i);
		if (sz != v.capacity())
		{
			sz = v.capacity();
			cout << "第" << cnt++<<"次扩容: " << sz << '\n';
		}
	}
}

c++11提供的接口,调用后缩容。

void Vector_Test6()
{
	vector<int> vv;
	cout << "capacity:" << vv.capacity() << endl;
	vv.reserve(20);
	cout << "capacity:" << vv.capacity() << endl;
	vv.resize(10);
	vv.shrink_to_fit();
	cout << "shrink_to_fit_capacity:" << vv.capacity() << endl;

}

元素访问 

上述两个接口的功能类似,在底层实现上【】检查越界的方式是断言,在release断言会失效。at接口底层检查越界的方式是抛异常,使用上可读性没有【】直观,因为我们比较习惯【】的使用。

void Vector_Test7()
{
	vector<int> vv = { 1,2,3,4,5,6,7,8,9 };
	cout <<"[]:>" << vv[3] << endl;
	cout <<"at:>"<< vv.at(5) << endl;
}

增删查改

assign:将新内容赋给向量,替换其当前内容,并相应地修改其大小。

void Vector_Test8()
{
	vector<int> vv = { 1,2,3,4,5,6,7,8,9 };
	
	vector<int> vv2;
	vv2.assign(vv.begin(), vv.end());
	Printf(vv);

	vv.assign(12, 1);
	Printf(vv);

}

上述剩余接口都比较常用,需要注意的是,在使用插入或者删除后如果后序还要使用it,需要重写接收一下insert和erase返回的迭代器位置,否则会出现迭代器失效的问题,后面会详细讨论。

void Vector_Test9()
{
	vector<int> vv = { 1,2,3,4,5,6,7,8,9 };
	auto it = vv.begin();
	vv.insert(it,5);
	vv.insert(it, 5);
	vv.insert(it, 5);
	vv.insert(it, 5);

	it++;
	*it = 10;
}

 查找接口统一使用算法中(algorithm)的查找,vector容器中并没有提供查找接口。

模拟实现

模拟实现要点图解

 整体代码

	template<class T>

	class vector
	{
	public:
		typedef T* iterator;
		typedef const T* const_iterator;
		iterator being()
		{
			return _start;
		}
		iterator end()
		{
			return _finish;
		}
		const_iterator begin() const
		{
			return _start;
		}
		const_iterator end()const
		{
			return _finish;
		}

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

		//无参构造
		vector()
			:_start(nullptr)
			, _finish(nullptr)
			, _end_of_storage(nullptr)
		{}
		//初始化n个val
		vector(size_t n, const T& val = T())
			:_start(nullptr)
			, _finish(nullptr)
			, _end_of_storage(nullptr)
		{
			reserve(n);
			for (int i = 0; i < n; ++i)
			{
				push_back(val);
			}
		}
			vector(int n, const T & val = T())
				:_start(nullptr)
				, _finish(nullptr)
				, _end_of_storage(nullptr)
			{
				reserve(n);
				for (int i = 0; i < n; ++i)
				{
					push_back(val);
				}
			}
		//代代器区间
		template <class InputIterator>
		vector(InputIterator first, InputIterator last)
			:_start(nullptr)
			, _finish(nullptr)
			, _end_of_storage(nullptr)
		{
			while (first != last)
			{
				push_back(*first);
				++first;
			}
		}
		//拷贝构造
		vector(const vector<T>& v)
			:_start(nullptr)
			, _finish(nullptr)
			, _end_of_storage(nullptr)
		{
			//调用构造
			vector<T> tmp(v.begin(),v.end());
			//将构造好的交换给this 
			swap(tmp);
		}
		//赋值,这里不能给引用,不然赋值变成交换
		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(_end_of_storage,v._end_of_storage);
		}
		~vector()
		{
			delete[] _start;
			_start = _finish = _end_of_storage = nullptr;
		}
		//扩容
		void reserve(size_t n)
		{
			if (n > capacity())
			{
				//扩容
				T* tmp = new T[n];
				size_t oldsize = size();

				if (_start)
				{
					//浅拷贝
					//memcpy(tmp, _start, sizeof(T) * oldsize);

					for (size_t i = 0; i < oldsize; i++)
					{
						tmp[i] = _start[i];
					}
					delete[] _start;
				}
				
				//_start的地址变成了新的
				_start = tmp;
				_finish = tmp + oldsize;//_finish的计算要注意
				_end_of_storage = tmp + n;
			}
		}
		//调整size
		void resize(size_t n, T val = T())
		{
			if(n>capacity())
			{
				//扩容
				reserve(n);
			}
			if (n > size())
			{
				//填数据
				while (_finish < _start + n)
				{
					* _finish = val;
					++_finish;
				}

			}
			else
			{
				//删除数据
				_finish = _start + n;
			}

		}
		//返回容量
		size_t capacity() const
		{
			return _end_of_storage - _start;
		}
		//返回size
		size_t size() const
		{
			return _finish - _start;
		}
		//尾插
		void push_back(const T& val)
		{
			if (_finish == _end_of_storage)
			{
				//扩容
				size_t newcapacity = capacity() == 0 ? 4 : 2 * capacity();
				reserve(newcapacity);
				_end_of_storage = _start + newcapacity;
			}
			*_finish = val;
			_finish++;
		}
		//尾删
		void pop_back()
		{
			assert(size()>0);
			--_finish;
		}

		//迭代器失效问题
		//插入
		iterator insert(iterator pos,const T& val)
		{
			assert(pos >= _start);
			assert(pos <= _finish);

			if (_finish == _end_of_storage)
			{
				//记录下pos到 ——start的距离
				size_t len = pos - _start;
				size_t newcapacity = capacity() == 0 ? 4 : 2 * capacity();
				reserve(newcapacity);

				//扩容后会导致迭代器的失效问题
				pos = _start + len;
			}

			//向后挪动数据
			iterator end = _finish - 1;
			while (end >= pos)
			{
				*(end+1) = *end;
				--end;
			}
			*pos = val;
			++_finish;
			return pos;
		}
		//删除
		iterator erase(iterator pos)
		{
			assert(pos >= _start);
			assert(pos < _finish);

			iterator begin = pos + 1;
			while (begin < _finish)
			{
				*(begin - 1) = *(begin);
				++begin;
			}

			--_finish;
			return pos;
		}
		//清除数据
		void clear()
		{
			_finish = _start;
		}
		//判断空
		bool empty() const
		{
			return _start == _finish;
		}

	private:
		iterator _start;
		iterator _finish;
		iterator _end_of_storage;

	};

迭代器失效问题

内部失效

外部失效

int main()
{
	vector<int> vv;
	auto it = vv.begin();

	vv.insert(it, 1);
	//读
	cout << *it << endl;

	//cout << (*it)++ << endl;
	return 0;
}

上述代码中用的是库中的vector,但是同样存在迭代器失效的问题,调用insert接口插入数据后会发生扩容,外部迭代器指向的空间已经被释放了,此时该迭代器已经失效了,当对该位置进行读写操作时就会出现错误!

如果仍然要再次使用it的话,在使用前对it重新赋值就好了。

深浅拷贝问题

void reserve(size_t n)
		{
			if (n > capacity())
			{
				//扩容
				T* tmp = new T[n];
				size_t oldsize = size();

				if (_start)
				{
					//浅拷贝
					//memcpy(tmp, _start, sizeof(T) * oldsize);

					delete[] _start;
				}
				
				//_start的地址变成了新的
				_start = tmp;
				_finish = tmp + oldsize;//_finish的计算要注意
				_end_of_storage = tmp + n;
			}
		}
void testcopy()
{
	zxy::vector<string> v1;

	string ss("abc");
	v1.push_back(ss);
	v1.push_back(ss);
	v1.push_back(ss);
	v1.push_back(ss);
	//第5次插入发生扩容
	v1.push_back(ss);

}

 问题分析:

当第5次插入数据,发生了扩容。string是自定义类型,并且有资源的申请。浅拷贝完成后,手动的delete[] _start。当析构函数调用时会在次释放该空间,所以这里不能用浅拷贝。

解决办法:换成深拷贝

	//扩容
		void reserve(size_t n)
		{
			if (n > capacity())
			{
				//扩容
				T* tmp = new T[n];
				size_t oldsize = size();

				if (_start)
				{
					for (size_t i = 0; i < oldsize; i++)
					{
						tmp[i] = _start[i];
					}
					delete[] _start;
				}
				
				//_start的地址变成了新的
				_start = tmp;
				_finish = tmp + oldsize;//_finish的计算要注意
				_end_of_storage = tmp + n;
			}
		}

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

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

相关文章

HTML5 <meta> 标签

HTML5 <meta> 标签 实例 描述 HTML 文档的元数据&#xff1a; <head> <meta name"description" content"免费在线教程"> <meta name"keywords" content"HTML,CSS,XML,JavaScript"> <meta name"auth…

全志v851s 在 eyesee-mpp 中添加一个hello_world sample 的流程

1. 为什么要在eyesee-mpp 中添加sample&#xff1f; 1&#xff09;保持整个openwrt 应用程序编写的完成性&#xff1b; 2&#xff09;eyesee-mpp 中包含了几乎所有全志视频音频模块的sample 以及 头文件&#xff0c;参考以及头文件调用起来非常方便&#xff0c;而且可以学习各种…

MongoDB 聚合管道中使用数组表达式运算符合并数组($concatArrays)

数组表达式运算符主要用于文档中数组的操作&#xff0c;接上一篇&#xff1a;MongoDB 聚合管道中使用数组表达式运算符&#xff08;$slice截取数组&#xff09;https://blog.csdn.net/m1729339749/article/details/130130328本篇我们主要介绍数组表达式运算符中用于合并数组的操…

InnoSetup制作安装包(EXE)

功能描述 1.666666.war为项目war包&#xff0c;666666.bat为启动war包脚本&#xff0c;通过InnoSetup将它们打包到安装包666666.exe 2.666666.exe安装包安装过程中将666666.bat注册为自启动服务&#xff0c;安装结束自动执行脚本启动项目666666.war --------------------------…

VxLAN数据中心L2/L3互通(端到端)

VxLAN数据中心端到端方式实现L2/L3互连&#xff0c;这种实现方式可以使数据中心属于同一个EVPN-VXLAN域&#xff0c;相较于hand-off方式通过端到端实现数据中心L2互连可以满足Mac mobility、ARP suppression等特性。 实现思路 DC1的Border-Leaf和DC2的Border-Leaf之间运行EBG…

测试:腾讯云轻量4核8G12M服务器CPU流量带宽系统盘

腾讯云轻量4核8G12M应用服务器带宽&#xff0c;12M公网带宽下载速度峰值可达1536KB/秒&#xff0c;折合1.5M/s&#xff0c;每月2000GB月流量&#xff0c;折合每天66GB&#xff0c;系统盘为180GB SSD盘&#xff0c;地域节点可选上海、广州或北京&#xff0c;4核8G服务器网来详细…

MySQL 日志

错误日志(error log): error log 主要记录 MySQL 在启动、关闭或者运行过程中的错误i西南西&#xff0c;在MySQL 的配置文件 my.cnf 中&#xff0c;可以通过 log-error/var/log/mysqld.log 执行 mysql 错误日志的位置 慢查询日志(slow query log): MySQL 的慢查询日志是 MyS…

【erlang】并发篇

PID类型 在之前的语法篇中&#xff0c;我们并没有介绍 PID这个类型&#xff0c;它和并发息息相关&#xff0c;因此我们在这里来学习它。 PID是进程标识符的意思&#xff0c;用来标识一个erlang进程。在所有相连的erlang节点中&#xff0c;PID都是唯一的。但是PID会被复用&…

从零搭建一个 Level-2 快照数据的因子计算平台

因子挖掘是量化交易的基础。近年来&#xff0c;Python 是很多研究员进行数据分析和因子挖掘的主流工具。但是通过 Python 挖掘的有效因子在投产时&#xff0c;通常需要由 QUANT 团队的研究员将代码提交给 IT 团队&#xff0c;IT 团队用 C 代码转写后部署到生产环境&#xff0c;…

1. HTMLCSS

文章目录1 盒子模型&#xff1a;1.1 盒子属性导图1.2 边框属性导图1.3 定位导图&#xff1a;2 HTML常用标签2.1 基本标签① HTML基本结构② HTML常见标签③ 特殊字符④ 列表标签a 无序列表&#xff1a;b 有序列表&#xff1a;⑤ 表单3 CSS快速上手3.1 background属性① 思维导图…

自适应遗传算法求解TSP问题(Java)

1 引言 普通遗传算法&#xff08;Sample Genetic Algorithm, SGA&#xff09;存在着严重的缺点&#xff0c;它的Pc和Pm的值是固定的&#xff0c;本文采用自适应遗传算法进行求解TSP问题。不管是优良个体还是劣质个体都经过了相同概率的交叉和变异操作。这会引起两个很严重的问…

Linux驱动开发——高级I/O操作(二)

目录 proc文件操作 非阻塞型I/O 阻塞型I/O proc文件操作 proc 文件系统是一种伪文件系统&#xff0c;这种文件系统不存在于磁盘上&#xff0c;只存在于内存中只有内核运行时才会动态生成里面的内容。这个文件系统通常挂载在/proc 目录下&#xff0c;是核开发者向用户导出信息…

公司招人,面试了50+的候选人,技术实在是太烂了····

前两个月&#xff0c;公司测试岗位面了 50候选人&#xff0c;面试下来发现几类过不了的情况&#xff0c;分享大家防止踩坑&#xff1a; 技术倒是掌握得挺多&#xff0c;但只是皮毛&#xff0c;基础知识却是一塌糊涂。工作多年&#xff0c;从未学习过工作之外的技术栈&#xff…

ERTEC200P-2 PROFINET设备完全开发手册(7-1)

7. 配置模块及自定义模块 7.1.1 PN设备的基本模型 初次接触PN的开发者&#xff0c;最容易出现的错误就是设备的实际配置与TIA的组态不一致。为了开发的过程更加顺利&#xff0c;非常有必要掌握PN设备的基础模型。PN设备的基本模型如下图描述&#xff1a; PN设备的基本构成是插…

No.039<软考>《(高项)备考大全》【第23章】综合测试管理

【第23章】综合测试管理1 章节相关1.1 考试相关1.2 案例相关2 测试监控3 测试风险管理4 测试人员绩效考核4.1 测试分类测试类型分类执行方式分类开发阶段分类5 开发测试分类参考答案1 章节相关 1.1 考试相关 必考1分选择&#xff0c;案例概率低。 1.2 案例相关 2020年下半年…

关于IOS系统时间格式显示NAN问题以及小程序项目运行报错app.json找不到

目录 问题一&#xff1a;关于IOS系统时间格式显示NAN 一、比较常见的情况&#xff0c;时间格式为"yyyy-MM-dd HH:mm:ss"格式在 iOS 会出现 NAN 二、关于时间临界值&#xff1a;对于00:00:00和24:00:00这两个时间临界值, ios会转成NAN 三、时间格式为2022/09&#…

Java语法理论和面经杂疑篇《六.泛型(Generic)》

1. 泛型概述 1.2 泛型的引入 在Java中&#xff0c;我们在声明方法时&#xff0c;当在完成方法功能时如果有未知的数据需要参与&#xff0c;这些未知的数据需要在调用方法时才能确定&#xff0c;那么我们把这样的数据通过形参表示。在方法体中&#xff0c;用这个形参名来代表那…

Medical X-rays Dataset汇总(长期更新)

目录​​​​​​​ ChestX-ray8 ChestX-ray14 VinDr-CXR VinDr-PCXR ChestX-ray8 ChestX-ray8 is a medical imaging dataset which comprises 108,948 frontal-view X-ray images of 32,717 (collected from the year of 1992 to 2015) unique patients with the text-mi…

Nginx(下载安装、常用命令、反向代理、负载均衡)

官网&#xff1a;https://nginx.org/Nginx是一款轻量级的Web服务器/反向代理服务器及电子邮件&#xff08;IMAP/POP3&#xff09;代理服务器,其特点是占有内存少&#xff0c;并发能力强。下载和安装下载在Nginx的官网的下载页面中(http://nginx.org/en/download.html)&#xff…

01、Cadence使用记录之新建工程与基础操作(原理图绘制:OrCAD Capture CIS)

01、Cadence使用记录之新建工程与基础操作&#xff08;原理图绘制&#xff1a;OrCAD Capture CIS&#xff09; 硕士学电磁场去了&#xff0c;写点博客记录下学习过程。 参考的教程是B站的视频&#xff1a;allegro软件入门视频教程全集100讲 本科的时候就对Cadence有所耳闻&am…