C++: vector

目录

1.vector的介绍

2.vector常用的接口 

1.vector构造

2.迭代器iterator的使用

3.vector空间增长

 4.vector的增删改查

3.vector模拟实现

如果在reverse时使用memcpy会怎么样?


1.vector的介绍

C++中的vector是一个动态数组容器,可以存储任意类型的数据。它提供了动态大小的数组功能,可以在运行时动态地增加或减少其大小。vector是C++标准模板库(STL)中的一部分,因此可以使用标准库中提供的许多函数和算法来操作它。

1. vector 是表示可变大小数组的序列容器。
2. 就像数组一样, vector 也采用的连续存储空间来存储元素。也就是意味着可以采用下标对 vector 的元素进行访问,和数组一样高效。但是又不像数组,它的大小是可以动态改变的,而且它的大小会被容器自动处理。
3. vector 分配空间策略: vector 会分配一些额外的空间以适应可能的增长,因为存储空间比实际需要的存 储空间更大。不同的库采用不同的策略权衡空间的使用和重新分配。但是无论如何,重新分配都应该是 对数增长的间隔大小,以至于在末尾插入一个元素的时候是在常数时间的复杂度完成的。
4. 与其它动态序列容器相比( deque, list and forward_list ), vector 在访问元素的时候更加高效,在末 尾添加和删除元素相对高效。对于其它不在末尾的删除和插入操作,效率比较低。

2.vector常用的接口 

1.vector构造

1.vector();无参构造函数

2.vector(size_type n,const value_type&val=value_type());

构造一个包含 n 个元素的容器。每个元素都是 val 

3.vector(const vector&x); 拷贝构造。

4.vector(InputIterator first,InputIterator last);

用迭代器进行初始化构造            

2.迭代器iterator的使用

1.iterator begin();

返回指向vector中第一个元素的迭代器。

2.iterator end();

返回指向vector中最后一个元素下一个位置的迭代器。

3.reverse_iterator rbegin()

返回指向vector中最后一个元素位置的reverse_iterator

4.reverse_iterator end()

                                               

 返回指向vector中第一个元素的前一个位置位置的reverse_iterator

3.vector空间增长

1.size_type size();

返回数据个数

2.size_type capacity(); 

返回容量大小

3.bool empty();

判断是否为空

4.resize函数

如果 n 小于当前容器大小,则内容将减少到其前 n 个元素,删除超出的元素(并销毁它们)。

如果 n 大于当前容器大小,则通过在末尾插入任意数量的元素来扩展内容,以达到 n 的大小。如果指定了 val,则新元素将初始化为 val 的副本,否则,它们将初始化值。

如果 n 也大于当前容器容量,则会自动重新分配分配的存储空间。

5.reverse函数

请求vector容量至少足以包含 n 个元素。

如果 n 大于当前向量容量,则该函数会导致容器重新分配其存储,从而将其容量增加到 n(或更大)。

在所有其他情况下,函数调用不会导致vector容量不受影响。

capacity 的代码在 vs g++ 下分别运行会发现, vs capacity 是按 1.5 倍增长的, g++ 是按 2 倍增长的
reserve 只负责开辟空间,如果确定知道需要用多少空间, reserve 可以缓解 vector增容的代价缺陷问题。
resize 在开空间的同时还会进行初始化,影响 size。

 4.vector的增删改查

 1.push_back();尾插

2.pop_back();尾删

3.find();查找

find是算法模块实现,不是vector的成员接口

template<class InputIterator, class T>
  InputIterator find (InputIterator first, InputIterator last, const T& val)
{
  while (first!=last) {
    if (*first==val) return first;
    ++first;
  }
  return last;
}

4.insert();插入

通过在指定位置的元素之前插入新元素来扩展vector,从而有效地通过插入的元素数增加容器大小。

当且仅当新的vector大小超过当前vector容量时,这会导致自动重新分配分配的存储空间。

由于vector使用数组作为其基础存储,因此在vector以外的位置插入元素会导致容器将位置之后的所有元素重新定位到其新位置。与其他类型的序列容器(如列表或forward_list)对相同操作执行的操作相比,这通常是一种低效的操作。

5.erase();删除

从向量中删除单个元素 或一系列元素

由于vector使用数组作为其基础存储,因此擦除vector以外的位置的元素会导致容器在擦除段后将所有元素重新定位到其新位置。与其他类型的序列容器对相同操作执行的操作相比,这通常是一种低效的操作 

6.swap();交换

通过 x 的内容交换容器的内容,x 是另一个相同类型的vector对象。尺寸可能有所不同。

调用此成员函数后,此容器中的元素是调用之前位于 x 中的元素,x 的元素是位于 this 中的元素。所有迭代器、引用和指针对交换的对象仍然有效。

3.vector模拟实现

#pragma once
#include<assert.h>


namespace wjc
{
	template <class T>
	class vector
	{
	public:
		typedef T* iterator;
		typedef const T* const_iterator;


		vector()
			: _start(nullptr)
			, _finish(nullptr)
			, _endofstorage(nullptr)
		{
		}
		vector(const vector<T>& v)
		{
			reserve(v.capacity());
			for (const auto& e : v)
			{
				push_back(e);
			}
		}
		iterator begin()
		{
			return _start;
		}


		iterator end()
		{
			return _finish;
		}
		const_iterator begin() const
		{
			return _start;
		}


		const_iterator end() const
		{
			return _finish;
		}
		size_t capacity()const
		{
			return _endofstorage - _start;
		}
		size_t size()const
		{
			return  _finish - _start;
		}


		void push_back(const T& a)
		{
			if (_finish == _endofstorage)
			{
				size_t newcapacity = capacity() == 0 ? 4 : capacity() * 2;
				reserve(newcapacity);
			}
			*_finish = a;
			_finish++;
		}


		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 oldsize = size();
				T* tmp = new T[n];
				if (_start)
				{
					/*memcpy(tmp, _start, sizeof(T) * oldsize);*/
					for (size_t i = 0; i < oldsize; i++)
					{
						tmp[i] = _start[i];
					}
					delete[] _start;
				}
				_start = tmp;
				_finish = _start + oldsize;
				_endofstorage = _start + n;
			}
		}
		void resize(size_t n, T val = T())
		{
			if (n > size())
			{
				reserve(n);
				while (_finish < _start + n)
				{
					*_finish = val;
					++_finish;
				}
			}
			else
			{
				_finish = _start + n;
			}
		}




		void pop_back()
		{
			assert(size() > 0);
			--_finish;
		}
		void insert(iterator pos, T x)
		{
			assert(pos <= _finish);
			assert(pos >= _start);
			size_t len = pos - _start;
			if (_finish == _endofstorage)
			{
				reserve(capacity() == 0 ? 4 : capacity() * 2);//pos ʧЧ
				//posλ
				pos = _start + len;
			}
			memmove(pos + 1, pos, (_finish - pos) * sizeof(T));
			*pos = x;
			++_finish;
		}


		void erase(iterator pos)
		{
			assert(pos < _finish);
			assert(pos >= _start);
			iterator it = pos + 1;
			while (it < _finish)
			{
				*(it - 1) = *it;
				++it;
			}
			_finish--;
		}


		T& operator[](size_t pos)
		{
			assert(pos, size());
			return _start[pos];
		}
		const T& operator[](size_t pos)const
		{
			assert(pos, size());
			return _start[pos];
		}
		template<class InputIterator>
		vector(InputIterator first, InputIterator last)
		{
			while (first != last)
			{
				push_back(*first);
				++first;
			}
		}
	private:


		iterator _start;
		iterator _finish;
		iterator _endofstorage;
	};


}

如果在reverse时使用memcpy会怎么样?

1. memcpy 是内存的二进制格式拷贝,将一段内存空间中内容原封不动的拷贝到另外一段内存空间中
2. 如果拷贝的是自定义类型的元素, memcpy 既高效又不会出错,但如果拷贝的是自定义类型元素,并且自定义类型元素中涉及到资源管理时,就会出错,因为memcpy 的拷贝实际是浅拷贝。
#include"vector.h"
int main()
{
	wjc::vector<string> v;
 	v.push_back("1111");
	v.push_back("2222");
	v.push_back("3333");
	v.push_back("4444");
	v.push_back("5555");
 	return 0;
}

运行这段代码会出现问题,因为插入4个元素后需要扩容,但是memcpy只是将一段内存空间中内容原封不动的拷贝到另外一段内存空间中,_start指向的空间已经释放了,也就是野指针,但是直到插入第5个元素_start依旧指向原来的空间,vector释放空间会导致同一片空间释放两次。

所以如果对象中涉及到资源管理时,千万不能使用memcpy进行对象之间的拷贝,因为memcpy浅拷贝,否则可能会引起内存泄漏甚至程序崩溃。最好开辟新空间来拷贝,如下面的方法:

void reserve(size_t n)
		{
			if (n > capacity())
			{
				size_t oldsize = size();
				T* tmp = new T[n];
				if (_start)
				{
					/*memcpy(tmp, _start, sizeof(T) * oldsize);*/
					for (size_t i = 0; i < oldsize; i++)
					{
						tmp[i] = _start[i];
					}
					delete[] _start;
				}
				_start = tmp;
				_finish = _start + oldsize;
				_endofstorage = _start + n;
			}
		}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【算法分析与设计】二叉树的层序遍历

&#x1f4dd;个人主页&#xff1a;五敷有你 &#x1f525;系列专栏&#xff1a;算法分析与设计 ⛺️稳中求进&#xff0c;晒太阳 题目 给你二叉树的根节点 root &#xff0c;返回其节点值的 层序遍历 。 &#xff08;即逐层地&#xff0c;从左到右访问所有节点&#xf…

Python 自动化办公:一键批量生成 PPT

Stata and Python 数据分析 一、导读 在实际工作中&#xff0c;经常需要批量处理Office文件&#xff0c;比如需要制作一个几十页的PPT进行产品介绍时&#xff0c;一页一页地制作不仅麻烦而且格式可能不统一。那么有什么办法可以一键生成PPT呢&#xff1f;Python提供的pptx 包…

Simulink|光伏并网逆变器低电压穿越仿真模型

目录 主要内容 模型研究 1.模型总览 2.boost模块 3.Inverter模块 4.控制模块 5.信号模块 结果一览 下载链接 主要内容 该模型为光伏逆变器低电压穿越仿真模型&#xff0c;采用boost加NPC拓扑结构&#xff0c;基于MATLAB/Simulink建模仿真。模型具备中点平衡…

灭火图 - 故障发现和定位的入口

通过深入分析和解决企业在可观测性和稳定性保障方面的挑战&#xff0c;Flashcat 提出了“灭火图”这一关键概念。 灭火图以服务/模块/基础组件/基础设施等为维度&#xff0c;以聚合的视角实时度量某个特定维度的可用性&#xff08;典型指标包括时延、流量、错误、饱和度&#x…

���恒峰|配网行波型故障预警定位装置:电力系统的守护神

&#xfffd;&#xfffd;&#xfffd;在电力系统中&#xff0c;设备的正常运行对于保障供电至关重要。而配网行波型故障预警定位装置就是电力系统的守护神&#xff0c;它能够实时监测设备状态&#xff0c;提前发现故障&#xff0c;确保电力供应的稳定。本文将详细介绍配网行波…

Gradle 笔记

Gradle依赖管理&#xff08;基于Kotlin DSL&#xff09; **注意&#xff1a;**如果不是工作原因或是编写安卓项目必须要用Gradle&#xff0c;建议学习Maven即可&#xff0c;Gradle的学习成本相比Maven高很多&#xff0c;而且学了有没有用还是另一回事&#xff0c;所以&#xff…

【网络】传输层TCP协议

目录 一、概述 2.1 运输层的作用引出 2.2 传输控制协议TCP 简介 2.3 TCP最主要的特点 2.4 TCP连接 二、TCP报文段的首部格式 三、TCP的运输连接管理 3.1 TCP的连接建立(三次握手) 3.2 为什么是三次握手&#xff1f; 3.3 为何两次握手不可以呢&#xff1f; 3.4 TCP的…

【KD】2023 NeurIPS Does Graph Distillation See Like Vision Dataset Counterpart?

简介 在大规模图数据集上进行GNN训练是一个艰巨的挑战。特别是在增量学习和图结构搜索这些经常需要重复训练的场景中,训练图模型不仅消耗大量时间,还对显存和计算能力提出了严峻要求。最近,图数据集蒸馏/图压缩(Graph Dataset Distillation / Graph Condensation)方法…

Harmony 鸿蒙驱动开发

驱动开发 驱动模型介绍 HDF&#xff08;Hardware Driver Foundation&#xff09;框架以组件化的驱动模型作为核心设计思路&#xff0c;为开发者提供更精细化的驱动管理&#xff0c;让驱动开发和部署更加规范。HDF框架将一类设备驱动放在同一个Host&#xff08;设备容器&#…

阿里巴巴开源联邦学习框架FederatedScope

5月5日&#xff0c;阿里巴巴达摩院发布新型联邦学习框架FederatedScope&#xff0c;声称可以在不共享训练数据的情况下开发机器学习算法&#xff0c;从而保护隐私。&#xff0c;其源代码现已在Apache 2.0许可下发布在GitHub上。 介绍 该平台被描述为一个全面的联邦学习框架&a…

compose部署tomcat

1.部署tomcat 1.1.下载相关镜像tomcat8.5.20 $ docker pull tomcat:8.5.20 1.2 在/data目录下创建tomcat/webapps目录 mkdir -p /data/tomcat/webapps 注意&#xff1a;这里是准备将宿主机的/data/tomcat/webapps映射到容器的 /usr/…

Oracle篇—分区表和分区索引的介绍和分类(第一篇,总共五篇)

☘️博主介绍☘️&#xff1a; ✨又是一天没白过&#xff0c;我是奈斯&#xff0c;DBA一名✨ ✌✌️擅长Oracle、MySQL、SQLserver、Linux&#xff0c;也在积极的扩展IT方向的其他知识面✌✌️ ❣️❣️❣️大佬们都喜欢静静的看文章&#xff0c;并且也会默默的点赞收藏加关注❣…

ChatGPT 引导语写法参考(翻译类引导语)

充当英语翻译和改进者 我想让你充当英文翻译员、拼写纠正员和改进员。我会用任何语言与你交谈&#xff0c;你会检测语言&#xff0c;翻译它并用我的文本的更正和改进版本用英文回答。我希望你用更优美优雅的高级英语单词和句子替换我简化的 A0 级单词和句子。保持相同的意思&am…

顶顶通呼叫中心中间件利用自动外呼进入机器人的压力测试配置流程

文章目录 前言呼入进入机器人配置流程呼入配置创建线路创建线路组创建自动外呼任务1. 实现“一端放音&#xff0c;另一端进入机器人”操作创建拨号方案—“模拟放音”呼叫路由—“internal”启用拨号方案—“模拟放音”队列外呼配置 2. 实现“两端都进入机器人”操作队列外呼配…

JavaWeb会议管理系统

相关技术&#xff1a; Servlet Tomcat jsp MySQL 有需要的可以联系我。 功能介绍&#xff1a; 会员管理系统&#xff1a;系统管理、用户管理、角色管理、菜单管理、日志管理、部门管理 会议管理&#xff1a;会议室管理、我的会员、会员纪要、修改密码、安全退出 会议室管…

C/C++读写文件和stringstream类

目录 C处理文件打开文件两种函数的区别 读文件两种函数区别其它读操作的函数fgetc&#xff1a;从文件中读取一个字符fgets&#xff1a;从文件中读取一个字符串fscanf&#xff1a;按格式从文件中读取指定内容&#xff0c;与scanf函数类似 写文件其它的常用写操作函数fputc&#…

【网站项目】基于SSM的263货物进销管理系统

&#x1f64a;作者简介&#xff1a;多年一线开发工作经验&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的项目或者毕业设计。 代码可以私聊博主获取。&#x1f339;赠送计算机毕业设计600个选题excel文件&#xff0c;帮助大学选题。赠送开题报告模板&#xff…

Java项目:基于ssm框架实现的电影评论系统(ssm+B/S架构+源码+数据库+毕业论文)

一、项目简介 本项目是一套ssm826基于ssm框架实现的电影评论系统&#xff0c;主要针对计算机相关专业的正在做毕设的学生与需要项目实战练习的Java学习者。 包含&#xff1a;项目源码、数据库脚本等&#xff0c;该项目附带全部源码可作为毕设使用。 项目都经过严格调试&#x…

Elasticsearch:2023 年 Lucene 领域发生了什么?

作者&#xff1a;来自 Elastic Adrien Grand 2023 年刚刚结束&#xff0c;又是 Apache Lucene 开发活跃的一年。 让我们花点时间回顾一下去年的亮点。 社区 2023 年&#xff0c;有&#xff1a; 5 个次要版本&#xff08;9.5、9.6、9.7、9.8 和 9.9&#xff09;&#xff0c;1 …

【CSP-J/S】复赛注意事项 上机文件组织形式

每年 CSP-J/S 复赛都有很多同学因为一些小失误导致一年的努力付之东流。Tony老师整理了一些复赛容易踩坑的点&#xff0c;或许对你有帮助&#xff01; 一、文件的输入输出 CSP、NOIP复赛与我们平时在Online Judge做题形式会有一些区别&#xff0c;需要我们将文件放入规定的地…