C++经典面试题 | 迭代器失效问题

版权声明:转用请注明出处 https://blog.csdn.net/weixin_39411321/article/details/89481378

STL中迭代器失效的问题

1. 关联性容器的迭代器失效问题

当删除一个STL容器(比如map, vector)中的某个元素时, 会引起迭代器失效, 所以, 我们务必提高警惕。 某次笔试, 我遇到这样一个题目: 删除map<int, int>中value为5的倍数的元素。 该题看起来很自然很简单, 实则有迭代器失效的陷阱。

如果对迭代器失效问题一无所知, 则很容易写出如下的错误代码:

#include <iostream>
#include <map>
using namespace std;
 
typedef map<int, int> Map;
typedef map<int, int>::iterator MapIt;
 
void print(Map &m)
{
	MapIt it;
	for(it = m.begin(); it != m.end(); it++)
	{
		cout << it->second << " ";
	}
 
	cout << endl;
}
 
void deleteValueFromMap(Map &m, int n = 5)
{
	MapIt it;
	for(it = m.begin(); it != m.end(); it++)
	{
		if(0 == it->second % n)
		{
			m.erase(it); //此时没有更新迭代器
		}
	}
}
 
int main()
{
	Map m;
	int i = 0;
	for(i = 0; i < 21; i++)
	{
		m[i] = i;
	}
 
	print(m);
 
	deleteValueFromMap(m); // 程序在此处崩溃
 
	return 0;
}

在这里插入图片描述
运行上述程序, 结果程序崩溃,打印不出来删除后的容器内容。 什么原因呢? 原来, 对于关联的容器map来说, m.erase(it);后, it就失效了, 而for循环中有it++, 自然而然会出问题啊。 那怎么办呢? 且看:

#include <iostream>
#include <map>
using namespace std;
 
typedef map<int, int> Map;
typedef map<int, int>::iterator MapIt;
 
void print(Map &m)
{
	MapIt it;
	for(it = m.begin(); it != m.end(); it++)
	{
		cout << it->second << " ";
	}
 
	cout << endl;
}
 
void deleteValueFromMap(Map &m, int n = 5)
{
	MapIt it;
	MapIt tmp;
	for(it = m.begin(); it != m.end(); /*不能再自增了*/)
	{
		if(0 == it->second % n)
		{
			tmp = it++;
			m.erase(tmp);
		}
		else
		{
			it++;
		}
	}
}
 
int main()
{
	Map m;
	int i = 0;
	for(i = 0; i < 21; i++)
	{
		m[i] = i;
	}
 
	print(m);
 
	deleteValueFromMap(m); // 程序ok
	print(m);
 
	return 0;
}

运行结果如下:
在这里插入图片描述
当然, 上述程序也可以继续简化为如下:

#include <iostream>
#include <map>
using namespace std;
 
typedef map<int, int> Map;
typedef map<int, int>::iterator MapIt;
 
void print(Map &m)
{
	MapIt it;
	for(it = m.begin(); it != m.end(); it++)
	{
		cout << it->second << " ";
	}
 
	cout << endl;
}
 
void deleteValueFromMap(Map &m, int n = 5)
{
	MapIt it;
	for(it = m.begin(); it != m.end(); /*不能再自增了*/)
	{
		if(0 == it->second % n)
		{
			m.erase(it++);
		}
		else
		{
			it++;
		}
	}
}
 
int main()
{
	Map m;
	int i = 0;
	for(i = 0; i < 21; i++)
	{
		m[i] = i;
	}
 
	print(m);
 
	deleteValueFromMap(m); // 程序ok
	print(m);
 
	return 0;
}

结果也OK!
在这里插入图片描述
肯定有人会问, m.erase(it++);就不会产生迭代器失效么?
确实不会! 为什么呢? 这样从it++说起了。

例如:
* ptr++ ;很多人错误地以为是先执行* p, 然后执行p++, 其实, 这是个天大的误解。 大家可以查一下* 和++的执行顺序, 虽然* 和++的优先级相同, 但此处采取的是右结合方式, 实际上先执行的是p++, 不过, p++的返回值是原来的p, 也就是说, *p++的值还是原值.

如果大家看过源码就知道他的内部大致是这样子的:

	temp=*p;
	p++;
	return temp;

所以, 在m.erase(it++);中,it++先执行, 此时还没有erase, 程序自然不会崩溃. 当it++执行完后, 已经指向了下一个元素了, 但it++的返回值还是当前元素, 此时再删除它, 合情合理。

2. 序列性容器的迭代器失效问题

#include <iostream>
#include <vector>
using namespace std;
 
typedef vector<int> Vec;
typedef vector<int>::iterator VecIt;
 
void print(Vec &v)
{
	VecIt it;
	for(it = v.begin(); it != v.end(); it++)
	{
		cout << *it << " ";
	}
 
	cout << endl;
}
 
void deleteValueFromVector(Vec &v, int n = 5)
{
	VecIt it;
	for(it = v.begin(); it != v.end(); it++)
	{
		if(0 == *it % n)
		{
			v.erase(it);
		}
	}
}
 
int main()
{
	Vec v;
	int i = 0;
	for(i = 0; i < 21; i++)
	{
		v.push_back(i); // 不能再傻傻地用下标了
	}
 
	print(v);
 
	deleteValueFromVector(v); // 程序崩溃
	print(v);
 
	return 0;
}

运行结果如下:
在这里插入图片描述
结果程序崩溃,打印不出来删除后的容器内容。
可见, vector也存在迭代器失效的问题, 怎么改呢? 如下:

#include <iostream>
#include <vector>
using namespace std;
 
typedef vector<int> Vec;
typedef vector<int>::iterator VecIt;
 
void print(Vec &v)
{
	VecIt it;
	for(it = v.begin(); it != v.end(); it++)
	{
		cout << *it << " ";
	}
 
	cout << endl;
}
 
void deleteValueFromVector(Vec &v, int n = 5)
{
	VecIt it;
	for(it = v.begin(); it != v.end(); /*不能再自增了*/)
	{
		if(0 == *it % n)
		{
			it=v.erase(it); // vector元素自动向前挪动了(关联的map容器元素不会自动挪动), 所以不能再把it进行++了
		}
		else
		{
			it++;
		}
	}
}
 
int main()
{
	Vec v;
	int i = 0;
	for(i = 0; i < 21; i++)
	{
		v.push_back(i); // 不能再傻傻地用下标了
	}
 
	print(v);
 
	deleteValueFromVector(v); // 程序ok
	print(v);
 
	return 0;
}

运行结果如下:
在这里插入图片描述

总结:

序列性容器::(vector和list和deque)

erase迭代器不仅使所指向被删元素的迭代器失效,而且使被删元素之后的所有迭代器失效,所以不能使用erase(iter++)的方式,但是erase的返回值为下一个有效的迭代器。

所以正确方法为::

  	for( iter = c.begin(); iter != c.end(); )
   		iter = c.erase(iter);

关联性容器::(map和set比较常用)

erase迭代器只是被删元素的迭代器失效,但是返回值为void,所以要采用erase(iter++)的方式删除迭代器,
所以正确方法为::

  	for( iter = c.begin(); iter != c.end(); ) 
  		c.erase(iter++);

猜你喜欢

转载自blog.csdn.net/weixin_39411321/article/details/89481378