其实vector的erase问题我以前自个写代码时就有遇到过,囧,只是又忘记了。
去曼恒数字面试时有一道笔试题大概是这样子的,问下面的程序运行后会有什么问题:
这个的问题在于itor被擦除后,for循环中又调用了itor,正确的写法要这样子:
下面这个文章对这个问题写得非常好,可以认真看一下。
http://blog.youkuaiyun.com/guafeng/archive/2008/06/20/2570313.aspx
STL vector的erase操作问题
一老大说优快云上有篇博文( “关于STL vector的erase操作”,地址是:http://blog.youkuaiyun.com/tingya/archive/2007/12/28/1998442.aspx)黑奇怪,抱着猎奇的心态,偶也去凑哈热闹,发现了一点问题,报告给大家。
作者说下面的代码是错误的:
vector<unsigned short> m_UintVector;
m_UintVector.push_back(10);
m_UintVector.push_back(20);
m_UintVector.push_back(30);
vector<unsigned short>::iterator itr;
itr = std::find(m_UintVector.begin(), m_UintVector.end(), 20);
m_UintVector.erase( itr);
作者给出了“正确”的代码:
vector<unsigned short> m_UintVector;
m_UintVector.push_back(10);
m_UintVector.push_back(20);
m_UintVector.push_back(30);
vector<unsigned short>::iterator itr;
itr = std::find(m_UintVector.begin(), m_UintVector.end(), 20);
//删除元素
int diff = itr - m_UintVector.begin();
m_UintVector.erase( m_UintVector.begin() + diff );
我倒没看到“正确”的代码高明在哪里?姑且不考虑代码是否正确,从代码量来看,明显增加了;从代码的通用性来看,iterator + diff 这样的代码只适用随机迭代器,对list, map等等非随机类型的迭代器是不能使用的。我觉得第一种写法更漂亮通用。至于代码的正确性,我测试过,工作得很好。
我猜想作者这里应该没有完整描述真实的工作环境,应该还有一些限制条件没有列出来,我google了一下,发现了下面的情况:
void main()
{
vector<int> member;
member.push_back(1);
member.push_back(2);
member.push_back(2);
member.push_back(3);
member.push_back(1);
member.push_back(2);
member.push_back(4);
vector<int>::iterator iter;
for(iter = member.begin();
iter != member.end();iter++)
cout<<*iter<<endl;
cout<<"do erase--------"<<endl;
for(iter = member.begin();
iter != member.end();iter++)
{
if(*iter == 2)
{
member.erase(iter);
}
}
for(iter = member.begin();
iter != member.end();iter++)
cout<<*iter<<endl;
}
乍一看,这不是和上面提到的第一种方法一样吗?貌似没有问题啊。可是,要注意到,调用erase后,回到for循环又继续使用迭代器,并执行++运算。
好,让我们再温习一下erase函数的说明:
iterator erase ( iterator position );
iterator erase ( iterator first, iterator last );
现在我们只关注函数执行后的副作用和返回值。函数调用后使指向position和first之后的所有迭代器失效。返回值则是一个指向删除的最后一个元素后面的元素的迭代器。所以上面代码中的iter在调用erase后就无效了,我在VS2005中测试了,确实崩溃在++的操作上。
要解决这个问题,我们只需弃用原来的迭代器,使用返回值即可,代码如下:
for(iter = member.begin(); iter != member.end();)
{
if(*iter == 2)
{
iter = member.erase(iter);
}
else
{
iter++;
}
}
网络上还有很多这样的文章都没太讲清楚,比如“如何删除std::vector内的element?”http://www.cnblogs.com/oomusou/archive/2006/11/15/561204.html 一文中说:若要删除std::vector中的element,正规的方式该用find() generic algorithm,若find()找到了,会传回该iterator,若找不到,将传回vector.end()。这种写法远比用for loop干净很多。
看他给出的代码:
// Compile OK, but run-time error!!
// for(std::vector<int>::iterator iter = ivec.begin();
// iter != ivec.end(); ++iter) {
// if (*iter == 8) {
// ivec.erase(iter);
// }
// }
// This is a RIGHT way to do it.
std::vector<int>::iterator iter = find(ivec.begin(), ivec.end(), 8);
if (iter != ivec.end()) {
ivec.erase(iter);
}
所以,网络上的文章看看就是了,最好是当作小说来看,猎奇一下别人的技术人生,至于是否真实是否正确,不要当真了,否则下一代堂吉诃德就要诞生了,呵呵。