【C++】vector的使用 以及 迭代器失效问题

这篇具有很好参考价值的文章主要介绍了【C++】vector的使用 以及 迭代器失效问题。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前言
经过前面string的学习,我们已经掌握了许多string的类函数,vector中许多类函数与string中的类函数使用起来相似,例如迭代器的使用在所有的容器中使用都一样,这里我们不再介绍,下面我们学习一些vector类的一些常用的函数。

1.vector的文档介绍

2. vector在C++中表示可变大小数组的序列容器,使用时需要包含头文件 < vector > ,就像数组一样,vector也采用的连续存储空间来存储元素。


一、 vector的构造函数

vector的构造函数主要有四个,下面我们来一 一演示

【C++】vector的使用 以及 迭代器失效问题

第一个是无参的默认构造,第二个是我们可以用n个val去初始化vector,第三个也就是vector的拷贝构造,第四个是使用迭代器进行初始化构造。

int main()
{
	vector<int> v1;
	v1.push_back(1); //push_back的作用就是插入一个元素
	v1.push_back(2);
	v1.push_back(3);
	v1.push_back(4);

	vector<int> v2(4); //用4个匿名对象进行初始化
	vector<int> v3(4, 9); //用4个9进行初始化
	vector<int> v4(++v1.begin(), --v1.end());//用迭代器进行进行初始化
	vector<int> v5(v3); //拷贝构造
	return 0;
}

观察监视:

【C++】vector的使用 以及 迭代器失效问题

二、容量相关的函数

【C++】vector的使用 以及 迭代器失效问题
我们先来看看前三个:

int main()
{
	vector<int> v1(10, 2);
	cout << "size:" << v1.size() << endl;
	cout << "capacity:" << v1.capacity() << endl;
	cout << "empty:" << v1.empty() << endl;
	return 0;
}

【C++】vector的使用 以及 迭代器失效问题

然后我们先来看reserve函数,reserve函数的作用就是开辟新的空间改变capacity,但是不会改变size

  • reserve函数中的 n小于原始的容量时,此函数什么也不做。
  • reserve函数中的n 大于原始的容量时,就会扩大容量,注意里面原有的数据不做改变。

我们来看下面的一段代码:

int main()
{
	vector<int> v1(10, 2);
	v1.reserve(5);
	v1.reserve(15);
	return 0;
}

观察监视窗口我们会发现,执行reserve(5)时函数什么都没有做,执行reserve(15)时函数才会进行扩容。

【C++】vector的使用 以及 迭代器失效问题

【C++】vector的使用 以及 迭代器失效问题

reserve只负责开辟空间,如果确定知道需要用多少空间,reserve可以缓解vector增容的代价缺陷问题。

下面我们再来看看resize函数,resize函数不仅会改变capacity也会改变size

  • 如果resize的参数n小于原始的size,那么就会保留前n个,后面的数据将会被销毁,但是capacity不变。

  • 如果resise的参数n大于原始的size,就会用第二个参数进行初始化size后面的空间,直到size == capacity

  • 如果resise的参数n大于原始的capacity,就会进行扩容并初始化未使用的空间。

我们来看下面一段代码:

int main()
{
	vector<int> v1(10, 2);
	v1.resize(5);
	v1.resize(15,7);
	return 0;
}

【C++】vector的使用 以及 迭代器失效问题

【C++】vector的使用 以及 迭代器失效问题

【C++】vector的使用 以及 迭代器失效问题

vector 空间增长问题在的讨论

关于vector空间增长机制我们可以用下面的代码来进行测试。

void TestVectorExpand()
{
	size_t sz;
	vector<int> v;
	//记录第一次的capacity的值
	sz = v.capacity();
	cout << "making v grow:\n";
	for (int i = 0; i < 100; ++i)
	{
		//挨个插入100个元素
		v.push_back(i);
		//如果容量发生了变化,就重新更新sz的数据,并打印sz
		if (sz != v.capacity())
		{
			sz = v.capacity();
			cout << "capacity changed: " << sz << '\n';
		}
	}
}

我们先来看一看windows下的扩容机制:

【C++】vector的使用 以及 迭代器失效问题

我们会发现windows平台下是1.5倍扩容!

我们再来看看Linux平台下的空间增长:

【C++】vector的使用 以及 迭代器失效问题
我们发现是2倍扩容。

原因是这两个平台采用的STL版本不同,vs是PJ版本STL,g++是SGI版本STL。

三、vector 数据的访问

【C++】vector的使用 以及 迭代器失效问题

代码演示:

int main()
{
	vector<int> v1;
	v1.push_back(0);
	v1.push_back(1);
	v1.push_back(2);
	v1.push_back(3);
	v1.push_back(4);

	cout << v1[2] << endl;
	cout << v1.at(2) << endl;
	cout << v1.front() << endl;
	cout << v1.back() << endl;
	int* pi = v1.data();
	pi[3] = 5;
	cout << pi[3] << endl;
	return 0;
}

【C++】vector的使用 以及 迭代器失效问题

四、vector的增删查改

【C++】vector的使用 以及 迭代器失效问题

1、assign函数

【C++】vector的使用 以及 迭代器失效问题
第一个函数的用一个迭代器区间进行赋值,第二个函数是用n个元素来进行赋值。

int main()
{
	vector<int> v1;
	v1.push_back(0);
	v1.push_back(1);
	v1.push_back(2);
	v1.push_back(3);
	v1.push_back(4);
	vector<int>::iterator it1 = v1.begin();
	while (it1 != v1.end())
	{
		cout << *it1;
		++it1;
	}
	cout << endl;

	vector<int> v2(2, 7);
	vector<int>::iterator it2 = v2.begin();
	while (it2 != v2.end())
	{
		cout << *it2 ;
		++it2;
	}
	cout << endl;
	
	//普通赋值
	v2.assign(3, 2);
	for (auto& e : v2)
	{
		cout << e ;
	}
	cout << endl;
	
	//用迭代器区间赋值
	v2.assign(v1.begin(), v1.end());
	for (auto& e : v2)
	{
		cout << e;
	}
	cout << endl;
	return 0;
}

【C++】vector的使用 以及 迭代器失效问题

2、insert函数

【C++】vector的使用 以及 迭代器失效问题
第一个函数是用1个元素来进行插入pos位置之前,第二个函数是用n个元素插入pos位置之前

int main()
{
	vector<int> v1;
	v1.push_back(0);
	v1.push_back(1);
	v1.push_back(2);
	v1.push_back(3);
	v1.push_back(4);

	v1.pop_back();
	for (auto& e : v1)
	{
		cout << e;
	}
	cout << endl;
	v1.insert(++v1.begin(), 2, 7);
	for (auto& e : v1)
	{
		cout << e;
	}
	cout << endl;
	return 0;
}

【C++】vector的使用 以及 迭代器失效问题

3、erase函数

【C++】vector的使用 以及 迭代器失效问题

第一个函数是删除pos迭代器位置。第二个是删除迭代器区间的所有元素

int main()
{
	vector<int> v1;
	v1.push_back(0);
	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.erase(++v1.begin(), --v1.end());

	for (auto& e : v1)
	{
		cout << e;
	}
	cout << endl;
	return 0;
}

【C++】vector的使用 以及 迭代器失效问题

4、swap函数

【C++】vector的使用 以及 迭代器失效问题

int main()
{
	vector<int> v1;
	v1.push_back(0);
	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(9, 7);
	v1.swap(v2);

	for (auto& e : v1)
	{
		cout << e;
	}
	cout << endl;
	return 0;
}

【C++】vector的使用 以及 迭代器失效问题

5、clear函数

【C++】vector的使用 以及 迭代器失效问题

int main()
{
	vector<int> v1;
	v1.push_back(0);
	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.clear();
	cout << "size:" << v1.size() << endl;
	cout << "capacity :" << v1.capacity() << endl;
	return 0;
}

【C++】vector的使用 以及 迭代器失效问题

6、find函数

find函数并不是vector里面的成员函数,而是C++里面的一个算法库里面的函数,其作用就是帮我们寻找我们想要的数据。

【C++】vector的使用 以及 迭代器失效问题
函数参数:两个迭代器,确定一个一个区间,最后面的参数是我们想要查找的元素。

返回值:当找到该元素后,返回该位置的迭代器,如果找不到,返回last迭代器。

注意:所有的迭代器区间都是左闭右开的,所以返回last迭代器代表找不到

int main()
{
	vector<int> v1;
	v1.push_back(0);
	v1.push_back(1);
	v1.push_back(2);
	v1.push_back(3);
	v1.push_back(4);

	//删除元素 2
	vector<int>::iterator it = find(v1.begin(), v1.end(), 2);
	v1.erase(it);
	for (auto& e : v1)
	{
		cout << e;
	}
	return 0;
}

【C++】vector的使用 以及 迭代器失效问题

五、vector 迭代器失效问题

迭代器的主要作用就是让算法能够不用关心底层数据结构,其底层实际上就是一个指针,或者是对指针进行了封装

迭代器失效,实际就是迭代器底层对应指针所指向的空间被销毁了,而使用一块已经被释放的空间,造成的后果可能未定义的或者是程序崩溃

但是由于vsg++采用的STL版本分别是PJ版本和SGI版本,因此对于迭代器的失效问题,在两个版本中的表现也不太相同。

迭代器的失效主要有两种方式:

  • 由于删除元素而导致的迭代器失效。
  • 由于容量改变而导致的迭代器失效

我们先来看下面由于删除元素而导致的迭代器失效:

int main()
{
	vector<int> v1{ 1,2,3,4,5 };
	vector<int>::iterator it = v1.begin();
	//删除it指向的元素
	v1.erase(it);
	//再对it指向的元素进行自增
	(*it)++;
	for (auto& e : v1)
	{
		cout << e ;
	}
	cout << endl;
	return 0;
}

erase删除it位置元素后,it位置之后的元素会往前搬移,但是我们再对it进行访问已经不合理了,因为最初时我们是想让it指向元素 1,元素1消失了,我们也不应该再对it进行访问了。

上面的代码在vs下的PJ版本会直接报错,而在g++下的SGI版本会访问下一个元素2

  • vs下执行(*it)++时会直接报错:
    【C++】vector的使用 以及 迭代器失效问题

  • g++下执行(*it)++时会直接访问后面移动到该位置上的元素,从而导致了导致2变成了3

    【C++】vector的使用 以及 迭代器失效问题

看到这里你可能觉得这里的迭代器好像也可以看成不失效啊,g++下还能通过it直接访问元素也是合理的啊,但是当我们上面的代码删除的是最后一个元素5,g++下还执行(*it)++时就发生了越界行为!

因此对于这个it如果我们还想使用我们就要对迭代器进行重新赋值,这也是解决迭代器失效的通用的方法。

下面我们再来看另外一种由于容量改变而导致的迭代器失效:

int main()
{
	vector<int> v1{ 1,2,3,4 };
	//it是未扩容之前的迭代器
	vector<int>::iterator it = v1.begin();

	v1.resize(50,0);
	//v1.begin()是扩容后的迭代器
	while (it != v1.end())
	{
		cout << *it << " ";
		++it;
	}
	cout << endl;
	return 0;
}
  • vs下执行时会直接报错:
    【C++】vector的使用 以及 迭代器失效问题

  • g++下执行时也会执行错误:

    【C++】vector的使用 以及 迭代器失效问题
    发生了许多越界访问。

这里迭代器失效的原因:是我们对v1进行了扩容,而it是指向没有扩容之前的开始位置的迭代器,扩容以后内存地址发生了变化,而it没有及时更新。我们还使用以前的迭代器就有可能造成许多越界行为。

【C++】vector的使用 以及 迭代器失效问题

对于第二种迭代器失效,只要会引起其底层空间改变的操作,都有可能是迭代器失效,比如:resize、reserve、insert、assign、push_back等。
因此同时使用可能引起其底层空间改变的函数和迭代器时要注意迭代器的更新!!文章来源地址https://www.toymoban.com/news/detail-423443.html

到了这里,关于【C++】vector的使用 以及 迭代器失效问题的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【C++庖丁解牛】STL之vector容器的介绍及使用 | vector迭代器的使用 | vector空间增长问题

    🍁你好,我是 RO-BERRY 📗 致力于C、C++、数据结构、TCP/IP、数据库等等一系列知识 🎄感谢你的陪伴与支持 ,故事既有了开头,就要画上一个完美的句号,让我们一起加油 vector的文档介绍 vector是表示可变大小数组的序列容器。 就像数组一样,vector也采用的连续存储空间来存

    2024年03月14日
    浏览(75)
  • 【c++】list迭代器失效问题

    目录 一、list iterator的使用 二、list的迭代器失效         对于list的迭代器的用法,可以将它看做一个指针(实际要更加复杂)来使用,该指针指向list中的一个节点。         【注意】         (1)begin和end为正向迭代器,对迭代器执行++操作,迭代器向后移动         (2)r

    2024年01月23日
    浏览(40)
  • C++笔记之迭代器失效问题处理

    code review! 参考博文:C++STL迭代器失效的几种情况总结 对于 std::vector 来说,擦除(erase)元素会导致迭代器失效。具体而言

    2024年02月14日
    浏览(38)
  • 【数据结构与算法】C++的STL模板(迭代器iterator、容器vector、队列queue、集合set、映射map)以及算法例题

    更多算法例题链接: 【数据结构与算法】递推法和递归法解题(递归递推算法典型例题) 什么是迭代器(iterator) 迭代器(iterator)的定义: 迭代器是一种检查容器内元素并遍历元素的数据类型。 迭代器提供对一个容器中的对象的访问方法,并且定义了容器中对象的范围。 容器

    2024年04月14日
    浏览(48)
  • STL之list模拟实现(反向迭代器讲解以及迭代器失效)

    这次是关于list的模拟实现的代码,先看看下面的代码: 上面是list的代码,其底层是一个带头双向循环的链表,实现的方法就不说了,相信大家已经都会了,然后自己实心的list我没有写析构函数等,这个也很简单,循环利用成员函数中的删除函数就可以。 先来说说个人认为

    2024年02月11日
    浏览(41)
  • C++中vector迭代器

    迭代器 (iterator)是一种用于遍历数据集合的的对象。它提供了一种访问数据集合中元素的方式,而无需暴露数据集合内部的细节。使用迭代器,我们可以对数据集合中的每个元素进行处理,而无需将整个数据集合加载到内存中。这样可以节省内存空间,并且在处理大型数据

    2024年02月11日
    浏览(45)
  • C++:vector使用以及模拟实现

    和我们原来讲的string不同, vector并不是类,是一个类模板,加类型实例化以后才是类。 vector是表示 可变大小数组 的序列容器。 像数组一样 ,vector也采用的连续存储空间来存储元素,但是容量可以动态改变。 和其它容器相比,vector访问元素、尾插、尾删较高效,但不在尾部

    2024年02月11日
    浏览(41)
  • 【C++】-vector的模拟实现(以及memcpy如何使用)

    💖作者:小树苗渴望变成参天大树🎈 🎉作者宣言:认真写好每一篇博客💤 🎊作者gitee:gitee✨ 💞作者专栏:C语言,数据结构初阶,Linux,C++ 动态规划算法🎄 如 果 你 喜 欢 作 者 的 文 章 ,就 给 作 者 点 点 关 注 吧! 今天来带大家看看vector的模拟实现,思路来说还是比较简

    2024年02月17日
    浏览(39)
  • C++ 什么时候使用 vector、list、以及 deque?

    什么时候使用 vector、list、以及 deque? 如果需要高效地快速访问(随即存取),并且不在乎插入和删除的效率,使用 vector 如果需要大量的插入和删除,而且不关心快速访问 (随即存取) ,使用 list 如果需要快速访问(随即存取) ,并且关心两端数据插入和删除,使用 deque 推荐一个零

    2024年02月10日
    浏览(34)
  • 【C++】链表(list)的使用以及与vector的区别

    在 C++ 中, std::list 是标准库提供的一个容器类,用于将数据进行链式存储。 链表 (list)是一种物理存储单元上非连续的存储结构,数据元素的逻辑顺序是通过链表中的指针链接实现的。 链表的组成:链表由一系列 结点 组成。 结点的组成:1.存储数据元素的 数据域 2.存储

    2024年02月06日
    浏览(42)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包