vector(3)

news2024/11/24 20:05:37

vector(3)

在这里插入图片描述

vector 迭代器失效问题。(重点)

迭代器的主要作用就是让算法能够不用关心底层数据结构,其底层实际就是一个指针,或者是对 指针进行了封装,比如:vector的迭代器就是原生态指针T 。因此迭代器失效,实际就是迭代器 底层对应指针所指向的空间被销毁了,而使用一块已经被释放的空间*,造成的后果是程序崩溃(即如果继续使用已经失效的迭代器,程序可能会崩溃)。

对于vector可能会导致其迭代器失效的操作有:

1.会引起其底层空间改变的操作,都有可能是迭代器失效

比如:resize、reserve、insert、 assign、push_back等.

这里拿insert来举例子:

vector.h

// 迭代器失效,本质因为一些原因,迭代器不可用
iterator insert(iterator pos, const T& x)
{
	assert(pos >= _start && pos <= _finish);

	if (_finish == _endofstorage)
	{
		size_t len = pos - _start;
		reserve(capacity() == 0 ? 4 : capacity() * 2);
		pos = _start + len;
	}

	iterator i = _finish - 1;
	while (i >= pos)
	{
		*(i + 1) = *i;
		--i;
	}

	*pos = x;
	++_finish;

	return pos;
}


void test_vector2()
{
	vector<int> v1;
	v1.push_back(1);
	v1.push_back(2);
	v1.push_back(3);
	v1.push_back(4);
	//v1.push_back(5);

	for (auto e : v1)
	{
		cout << e << " ";
	}
	cout << endl;

	v1.insert(v1.begin(), 30);

	for (auto e : v1)
	{
		cout << e << " ";
	}
	cout << endl;

这上面的代码是正确的版本,也就是可以运行的版本

但是我们平常在写代码也会遇到好多好多问题,以下是一些常见问题:

在这里插入图片描述

这里的错误在于实参传递给形参,但是形参的改变不会影响实参,这里传递给前面的begin,函数的返回值,传递的是拷贝而不是值本身,因为拷贝相当于是临时对象,临时对象具有常性,就相当于被const修饰,权限给缩小了

还有哪些情况会导致产生临时对象? 如类型转换

2.指定位置元素的删除操作–erase

erase删除pos位置元素后,pos位置之后的元素会往前搬移,没有导致底层空间的改变,理 论上讲迭代器不应该会失效,但是:如果pos刚好是最后一个元素,删完之后pos刚好是end 的位置,而end位置是没有元素的,那么pos就失效了。因此删除vector中任意位置上元素 时,vs就认为该位置迭代器失效了。

以下代码的功能是删除vector中所有的偶数,请问那个代码是正确的,为什么?

代码1:

#include <iostream>
using namespace std;
#include <vector>
int main()
{
vector<int> v{ 1, 2, 3, 4 };
auto it = v.begin();
while (it != v.end())
{
if (*it % 2 == 0)
v.erase(it);
++it;
}
return 0;
}

代码2:

#include <iostream>
using namespace std;
#include <vector>
int main()
{
vector<int> v{ 1, 2, 3, 4 };
auto it = v.begin();
while (it != v.end())
{
if (*it % 2 == 0)
it = v.erase(it);
else
++it;
}
return 0;
}

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

注意:Linux下,g++编译器对迭代器失效的检测并不是非常严格,处理也没有vs下极端。

// 1. 扩容之后,迭代器已经失效了,程序虽然可以运行,但是运行结果已经不对了
int main()
{
vector<int> v{1,2,3,4,5};
for(size_t i = 0; i < v.size(); ++i)
cout << v[i] << " ";
cout << endl;
auto it = v.begin();
cout << "扩容之前,vector的容量为: " << v.capacity() << endl;
// 通过reserve将底层空间设置为100,目的是为了让vector的迭代器失效
v.reserve(100);
cout << "扩容之后,vector的容量为: " << v.capacity() << endl;
// 经过上述reserve之后,it迭代器肯定会失效,在vs下程序就直接崩溃了,但是linux
下不会
// 虽然可能运行,但是输出的结果是不对的
while(it != v.end())
{
cout << *it << " ";
++it;
}
cout << endl;
return 0;
}
程序输出:
1 2 3 4 5
扩容之前,vector的容量为: 5
扩容之后,vector的容量为: 100
0 2 3 4 5 409 1 2 3 4 5
// 2. erase删除任意位置代码后,linux下迭代器并没有失效
// 因为空间还是原来的空间,后序元素往前搬移了,it的位置还是有效的
#include <vector>
#include <algorithm>
int main()
{
vector<int> v{1,2,3,4,5};
vector<int>::iterator it = find(v.begin(), v.end(), 3);
v.erase(it);
cout << *it << endl;
while(it != v.end())
{
cout << *it << " ";
++it;
}
cout << endl;
return 0;
}
程序可以正常运行,并打印:
4
4 5
// 3: erase删除的迭代器如果是最后一个元素,删除之后it已经超过end
// 此时迭代器是无效的,++it导致程序崩溃
int main()
{
vector<int> v{1,2,3,4,5};
// vector<int> v{1,2,3,4,5,6};
auto it = v.begin();
while(it != v.end())
{
if(*it % 2 == 0)
v.erase(it);
++it;
}
for(auto e : v)
cout << e << " ";
cout << endl;
return 0;
}

迭代器失效解决办法:在使用前,对迭代器重新赋值即可。

vector 空间增长问题

resize

改变vector的size

代码示例如下:

void test_vector6()
{
	vector<int> v1;
	v1.push_back(1);
	v1.push_back(2);
	v1.push_back(3);
	v1.push_back(4);
	for (auto e : v1)
	{
		cout << e << " ";
	}
	cout << endl;

	v1.resize(10);
	for (auto e : v1)
	{
		cout << e << " ";
	}
	cout << endl;

	v1.resize(15, 1);
	for (auto e : v1)
	{
		cout << e << " ";
	}
	cout << endl;

	v1.resize(2);
	for (auto e : v1)
	{
		cout << e << " ";
	}
	cout << endl;
}

功能和在string里面差不多

拷贝构造

void test_vector7()
{
	vector<int> v1;
	v1.push_back(1);
	v1.push_back(2);
	v1.push_back(3);
	v1.push_back(4);
	for (auto e : v1)
	{
		cout << e << " ";
	}
	cout << endl;

	vector<int> v2(v1);
	for (auto e : v1)
	{
		cout << e << " ";
	}
	cout << endl;
}

这里我们没有自己写拷贝构造的话,编译器会有自己生成的拷贝构造进行调用,也就是浅拷贝,值进行拷贝过去,

在这里插入图片描述

这样子析构的时候,会析构两次,程序出现问题

所以需要自己写深拷贝

vector(const vector<T>&v)
{
	reserve(v.capacity());//这里是以防v1里面的数据过多,需要用reserve预留开辟一块空间
    for(auto &e:v)//这里用了范围for,用到了引用,e是v1的别名,将v1里面的元素进行遍历
    {
        push_back(e);//尾插到v2里面
    }
}

赋值拷贝

代码如下:(现代写法)

void swap(vector<T>& tmp)
{
	std::swap(_start, tmp._start);
	std::swap(_finish, tmp._finish);
	std::swap(_endofstorage, tmp._endofstorage);
}

// v1 = v3
vector<T>& operator=(vector<T> v)
{
	swap(v);
	return *this;
}

vector<int> v3 = { 10,20,30,40 };
v1 = v3;

for (auto e : v1)
{
	cout << e << " ";
}
cout << endl;

区间构造迭代器

// 类模板的成员函数,也可以是一个函数模板
template <class InputIterator>
vector(InputIterator first, InputIterator last)
{
	while (first != last)
	{
		push_back(*first);
		++first;
	}
}

// vector<double> v4(10, 1.1);
// vector<int> v4(10, 1);
vector(size_t n, const T& val = T())
{
	reserve(n);
	for (size_t i = 0; i < n; i++)
	{
		push_back(val);
	}
}
//这里要将n前面的size_t改为int ,因为编译器会选择更其最匹配的进行调用
vector(int n, const T& val = T())
{
	reserve(n);
    //这里也需要注意
	for (int i = 0; i < n; i++)
	{
		push_back(val);
	}
}

vector(initializer_list<T> il)
{
	reserve(il.size());

	for (auto& e : il)
	{
		push_back(e);
	}
}

只有int会有这样的问题,出现不匹配的问题,别的类型不会

使用memcpy拷贝问题

  1. memcpy是内存的二进制格式拷贝,将一段内存空间中内容原封不动的拷贝到另外一段内存 空间中
  2. 如果拷贝的是自定义类型的元素,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;
	}
}


void test_vector9()
{
	vector<string> v1;
	v1.push_back("1111111111111111");
	v1.push_back("1111111111111111");
	v1.push_back("1111111111111111");
	v1.push_back("1111111111111111");
	v1.push_back("1111111111111111");

	for (auto e : v1)
	{
		cout << e << " ";
	}
	cout << endl;
}

在这里插入图片描述

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

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

相关文章

sql server 用户只读表权限

新建登录名 数据库建用户 用户赋予登录名和架构 赋予用户只读权限 GRANT SELECT ON Users TO gt

Vue——Uniapp回到顶部悬浮按钮

代码示例 <template><view class"updata" click"handleup" :style"{bottom: bottomTypepx}" ><i class"iconfont icon-huidaodingbu"></i></view> </template><script> export default {n…

利用弹性盒子完成移动端布局(第二次实验作业)

需要实现的效果如下&#xff1a; 下面是首先是这个项目的框架&#xff1a; 然后是html页面的代码&#xff1a; <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"wid…

解决element-ui图标不出现,或者乱码问题(已解决)复制粘贴

其实就是资源没找到&#xff0c;需要你手动添加。 下载个文件 通过百度网盘分享的文件&#xff1a;css 链接&#xff1a;https://pan.baidu.com/s/1jLngnKV3PuDYu2ohSlE5IQ?pwdt1z9 提取码&#xff1a;t1z9 https://pan.baidu.com/s/1jLngnKV3PuDYu2ohSlE5IQ?pwdt1z9 提取…

Python_函数式编程(生成器、迭代器、动态性)

简单说&#xff1a;时间换空间&#xff01;想要得到庞大的数据&#xff0c;又想让它占用空间少&#xff0c;那就用生成器&#xff01;延迟计算&#xff01;需要的时候&#xff0c;再计算出数据&#xff01; 创建生成器的方式二(生成器函数)生成器函数&#xff1a; 如果一个函数…

Spirng事务的传播学习

事务传播&#xff1a;一个事务方法在被调用时&#xff0c;如何与现有事务的交互行为。当方法被事务性地调用时&#xff0c;他应该加入当前事务还是开启一个新事物。 常见的事务传播机制&#xff08;7种&#xff09;&#xff1a; Propagation枚举类&#xff0c;定义了传播机制…

【D3.js in Action 3 精译_034】4.1 D3 中的坐标轴的创建(中一)

当前内容所在位置&#xff08;可进入专栏查看其他译好的章节内容&#xff09; 第一部分 D3.js 基础知识 第一章 D3.js 简介&#xff08;已完结&#xff09; 1.1 何为 D3.js&#xff1f;1.2 D3 生态系统——入门须知1.3 数据可视化最佳实践&#xff08;上&#xff09;1.3 数据可…

文件与fd

访问文件前&#xff0c;为什么必须要打开文件&#xff1f;/ 打开文件的实质 访问文件前&#xff0c;都必须先打开它&#xff0c; 如fopen 访问文件时&#xff0c;是进程在访问 所以文件必须加载到内存中 我们要访问文件时&#xff0c;一定要通过内存访问 文件没有被打开时&am…

UML(统一建模语言)

面向对象设计主要就是使用UML的类图&#xff0c;类图用于描述系统中所包含的类以及它们之间的相互关系&#xff0c;帮助人们简化对系统的理解&#xff0c;它是系统分析和设计阶段的重要产物&#xff0c;也是系统编码和测试的重要模型依据。 画图软件&#xff1a;ProcessOn思维…

人工智能和机器学习之线性代数(一)

人工智能和机器学习之线性代数&#xff08;一&#xff09; 人工智能和机器学习之线性代数一将介绍向量和矩阵的基础知识以及开源的机器学习框架PyTorch。 文章目录 人工智能和机器学习之线性代数&#xff08;一&#xff09;基本定义标量&#xff08;Scalar&#xff09;向量&a…

通过观测云 DataKit Extension 接入 AWS Lambda 最佳实践

前言 AWS Lambda 是一项计算服务&#xff0c;使用时无需预配置或管理服务器即可运行代码。AWS Lambda 只在需要时执行代码并自动缩放。借助 AWS Lambda&#xff0c;几乎可以为任何类型的应用程序或后端服务运行代码&#xff0c;而且无需执行任何管理。 Lambda Layer 是一个包…

5 个免费高清无水印视频素材库

短视频创作&#xff0c;素材是关键。以下为你推荐 5 个超棒的免费且无版权的高清无水印短视频素材网站&#xff0c;助你获取创作资源。 蛙学网 国内顶级素材站&#xff0c;有海量高质量素材&#xff0c;领域涉及自然风光、情感生活、游戏动漫以及社会人文等。其素材均为 4K 高…

集合框架07:LinkedList使用

1.视频链接&#xff1a;13.14 LinkedList使用_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV1zD4y1Q7Fw?spm_id_from333.788.videopod.episodes&vd_sourceb5775c3a4ea16a5306db9c7c1c1486b5&p142.LinkedList集合的增删改查操作 package com.yundait.Demo01;im…

Vmware开箱即用win7系统

下载文件 下载VMWare与win7成品虚拟机资料&#xff08;PS&#xff1a;里面有Win10 和Win11&#xff0c;使用方法都是一样的&#xff09; ⏬下载链接⏬ 下载链接 使用虚拟机打开成品虚拟机

python怎么引用文件

新建python文件 &#xff1a;在同目录lib下创建mylib.py和loadlib.py两个文件。 在mylib.py文件中创建一个Hello的类&#xff0c;并且给这个类添加一个sayHello的方法&#xff0c;让它输出hello python 在loadlib.py 文件中引入mylib import mylib 在loadlib中调用引用过来的py…

QT开发--串口通信

第十六章 串口通信 16.1 串口通信基础 串口通信主要通过DB9接口&#xff0c;适用于短距离&#xff08;<10米&#xff09;。关键参数包括&#xff1a; 波特率&#xff1a;每秒传输bit数&#xff0c;如9600。数据位&#xff1a;信息包中的有效数据位数。停止位&#xff1a;…

Excel:vba实现禁止新增工作表

实现效果&#xff1a;禁止新增工作表 步骤如下&#xff1a; 1.点击开发工具里面的Visual Basic 2.不要自己创建&#xff0c;点击ThisWorkbook&#xff0c;点击选择Workbook&#xff0c;点击选择NewSheet 这里的NewSheet就是工作簿事件 代码如下&#xff1a; 这是事件处理程序…

day∞-过滤器-拦截器

一、过滤器 二、拦截器

【Java面试——基础知识——Day5】

1. 异常 1.1 Exception 和 Error 有什么区别&#xff1f; 在 Java 中&#xff0c;所有的异常都有一个共同的祖先 java.lang 包中的 Throwable 类。Throwable 类有两个重要的子类: Exception :程序本身可以处理的异常&#xff0c;可以通过 catch 来进行捕获。Exception 又可以…

二叉树LeetCode刷题

二叉树LeetCode刷题 1. 检查两颗树是否相同2. 另一颗树的子树3. 翻转二叉树4. 判断一颗二叉树是否是平衡二叉树5. 二叉搜索树与双向链表6. 对称二叉树7. 二叉树的构建及遍历8. 二叉树的分层遍历9. 给定一个二叉树, 找到该树中两个指定节点的最近公共祖先10. 根据一棵树的前序遍…