7
std::vector<int> ints;

// ... fill ints with random values

for(std::vector<int>::iterator it = ints.begin(); it != ints.end(); )
{
    if(*it < 10)
    {
        *it = ints.back();
        ints.pop_back();
        continue;
    }
    it++;
}

此代码不起作用,因为pop_back()调用时it无效。但我没有找到任何关于std::vector::pop_back().

你有一些关于那个的链接吗?

4

11 回答 11

15

调用pop_back()删除向量中的最后一个元素,因此该元素的迭代器无效。该pop_back()调用不会使最后一个元素之前的项目的迭代器无效,只有重新分配才会这样做。来自 Josuttis 的“C++ 标准库参考”:

插入或删除元素会使引用以下元素的引用、指针和迭代器无效。如果插入导致重新分配,它会使所有引用、迭代器和指针无效。

于 2008-09-15T12:51:20.410 回答
13

这是您的答案,直接来自《神圣标准》:

23.2.4.2 载体满足容器和可逆容器(在 23.1 中的两个表格中给出)和序列的所有要求,包括大多数可选的序列要求 (23.1.1)。
23.1.1.12 表 68 表达式 a.pop_back() 返回 typevoid 操作语义a.erase(--a.end()) 容器向量、列表、双端队列

注意 a.pop_back 等价于 a.erase(--a.end())。查看vector关于擦除的细节:

23.2.4.3.3 - 迭代器擦除(迭代器位置) - 效果 -使擦除点之后的所有迭代器和引用无效

因此,一旦您调用 pop_back,任何指向先前最终元素(现在不再存在)的迭代器都将失效。

查看您的代码,问题是当您删除最后一个元素并且列表变为空时,您仍然递增它并离开列表的末尾。

于 2008-09-15T13:25:29.547 回答
5

(我使用 C++0x 工作草案中使用的编号方案,可在此处获得

第 732 页的表 94 说 pop_back(如果它存在于序列容器中)具有以下效果:

{ iterator tmp = a.end(); 
--tmp; 
a.erase(tmp); } 

23.1.1 第 12 点指出:

除非另有规定(显式或通过根据其他函数定义函数),调用容器成员函数或将容器作为参数传递给库函数不应使该容器内对象的迭代器无效或更改其值.

访问 end() 作为应用前缀 - 都没有这样的效果,但擦除():

23.2.6.4(关于 vector.erase() 第 4 点):

效果:在擦除点或擦除点之后使迭代器和引用无效。

所以总而言之:根据标准,pop_back() 只会使最后一个元素的迭代器无效。

于 2008-09-15T13:29:12.410 回答
3

这是来自 SGI 的 STL 文档 ( http://www.sgi.com/tech/stl/Vector.html )的引用:

[5] 向量的迭代器在其内存被重新分配时失效。此外,在向量中间插入或删除元素会使指向插入或删除点之后元素的所有迭代器无效。因此,如果您使用 reserve() 预分配与向量将使用的内存一样多的内存,并且所有插入和删除都在向量的末尾,则可以防止向量的迭代器失效。

我认为 pop_back 只会使指向最后一个元素的迭代器和 end() 迭代器无效。我们确实需要查看代码失败的数据,以及它无法确定发生了什么的方式。据我所知,代码应该可以工作 - 此类代码中的常见问题是迭代器上的元素和 ++ 的删除发生在同一迭代中,@mikhaild 指出的方式。但是,在这段代码中,情况并非如此:调用 pop_back 时不会发生 it++。

当它指向最后一个元素并且最后一个元素小于 10 时,仍然可能会发生一些不好的事情。我们现在正在比较一个无效的it 和 end()。它可能仍然有效,但不能保证。

于 2008-09-15T13:13:53.100 回答
1

迭代器仅在重新分配存储时失效。谷歌是你的朋友:见脚注 5

您的代码由于其他原因无法正常工作。

于 2008-09-15T12:33:20.313 回答
1

pop_back()仅使指向最后一个元素的迭代器无效。来自 C++ 标准库参考:

插入或删除元素会使引用以下元素的引用、指针和迭代器无效。如果插入导致重新分配,它会使所有引用、迭代器和指针无效。

因此,要回答您的问题,不,它不会使所有迭代器无效。

但是,在您的代码示例中,it当它指向最后一个元素并且值低于 10 时,它可能会失效。在这种情况下,Visual Studio 调试 STL 会将迭代器标记为无效,并进一步检查它是否不等于 end()将显示一个断言。

如果迭代器被实现为纯指针(就像在所有非调试 STL 向量情况下一样),您的代码应该可以正常工作。如果迭代器不仅仅是指针,那么您的代码将无法正确处理这种删除最后一个元素的情况。

于 2008-09-15T14:01:24.983 回答
0

错误是当“it”指向vector的最后一个元素并且如果这个元素小于10,这个最后一个元素被删除。现在“it”指向 ints.end(),下一个“it++”将指针移动到 ints.end()+1,所以现在“it”从 ints.end() 中逃跑,你得到了无限循环扫描你所有的记忆 :)。

于 2008-09-15T12:47:52.710 回答
0

“官方规范”是 C++ 标准。如果您无法访问 C++03 的副本,您可以从委员会的网站获取最新的 C++0x 草案:http ://www.open-std.org/jtc1/sc22/wg21/文档/论文/2008/n2723.pdf

容器要求的“操作语义”部分指定 pop_back() 等价于 { iterator i = end(); - 一世; 擦除(一);}。擦除的 [vector.modifiers] 部分显示“效果:在擦除点或之后使迭代器和引用无效。”

如果你想要直觉参数,pop_back 是没有失败的(因为在标准容器中破坏 value_types 是不允许抛出异常的),所以它不能做任何复制或分配(因为它们可以抛出),这意味着你可以猜到擦除元素的迭代器和结束迭代器无效,但其余部分无效。

于 2008-09-15T13:21:23.130 回答
0

pop_back() 只有在指向向量中的最后一项时才会使其无效因此,只要向量中的最后一个 int 小于 10,您的代码就会失败,如下所示:

*it = ints.back(); // 将 *it 设置为它已有的值
ints.pop_back(); // 使迭代器失效
continue; // 循环并访问无效的迭代器

于 2008-09-15T13:44:20.733 回答
0

您可能需要考虑使用擦除的返回值,而不是将后退元素交换到删除的位置并弹出。对于序列,擦除返回一个迭代器,该迭代器指向被删除元素之外的元素。请注意,此方法可能会导致比您的原始算法更多的复制。

for(std::vector<int>::iterator it = ints.begin(); it != ints.end(); )
{
    if(*it < 10)
        it = ints.erase( it );
    else
        ++it;
}

std::remove_if也可能是替代解决方案。

struct LessThanTen { bool operator()( int n ) { return n < 10; } };

ints.erase( std::remove_if( ints.begin(), ints.end(), LessThanTen() ), ints.end() );

std::remove_if(就像我的第一个算法一样)稳定,所以它可能不是最有效的方法,但它很简洁。

于 2009-01-06T08:45:21.050 回答
-1

在此处查看信息(cplusplus.com)

删除最后一个元素

删除向量中的最后一个元素,有效地将向量大小减一并使所有迭代器和对它的引用无效。

于 2008-09-15T12:40:48.407 回答